github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/docs/commands/for.md (about)

     1  # `for`
     2  
     3  > A more familiar iteration loop to existing developers
     4  
     5  ## Description
     6  
     7  This `for` loop is fills a small niche where `foreach` or `formap` are
     8  inappropiate in your script. It's generally not recommended to use `for`
     9  because it performs slower and doesn't adhere to Murex's design
    10  philosophy. However it does offer additional flexibility around recursion.
    11  
    12  ## Usage
    13  
    14  ```
    15  for ( variable; conditional; incrementation ) { code-block } -> <stdout>
    16  ```
    17  
    18  ## Examples
    19  
    20  ```
    21  » for ( i=1; i<6; i++ ) { echo $i }
    22  1
    23  2
    24  3
    25  4
    26  5
    27  ```
    28  
    29  ## Detail
    30  
    31  ### Syntax
    32  
    33  `for` is a little naughty in terms of breaking Murex's style guidelines due
    34  to the first parameter being entered as one string treated as 3 separate code
    35  blocks. The syntax is like this for two reasons:
    36    
    37  1. readability (having multiple `{ blocks }` would make scripts unsightly
    38  2. familiarity (for those using to `for` loops in other languages
    39  
    40  The first parameter is: `( i=1; i<6; i++ )`, but it is then converted into the
    41  following code:
    42  
    43  1. `let i=0` - declare the loop iteration variable
    44  2. `= i<0` - if the condition is true then proceed to run the code in
    45  the second parameter - `{ echo $i }`
    46  3. `let i++` - increment the loop iteration variable
    47  
    48  The second parameter is the code to execute upon each iteration
    49  
    50  ### Better `for` loops
    51  
    52  Because each iteration of a `for` loop reruns the 2nd 2 parts in the first
    53  parameter (the conditional and incrementation), `for` is very slow. Plus the
    54  weird, non-idiomatic, way of writing the 3 parts, it's fair to say `for` is
    55  not the recommended method of iteration and in fact there are better functions
    56  to achieve the same thing...most of the time at least.
    57  
    58  For example:
    59  
    60  ```
    61  a [1..5] -> foreach i { echo $i }
    62  1
    63  2
    64  3
    65  4
    66  5
    67  ```
    68  
    69  The different in performance can be measured. eg:
    70  
    71  ```
    72  » time { a [1..9999] -> foreach i { out <null> $i } }
    73  0.097643108
    74  
    75  » time { for ( i=1; i<10000; i=i+1 ) { out <null> $i } }
    76  0.663812496
    77  ```
    78  
    79  You can also do step ranges with `foreach`:
    80  
    81  ```
    82  » time { for ( i=10; i<10001; i=i+2 ) { out <null> $i } }
    83  0.346254973
    84  
    85  » time { a [1..999][0,2,4,6,8],10000 -> foreach i { out <null> $i } }
    86  0.053924326
    87  ```
    88  
    89  ...though granted the latter is a little less readable.
    90  
    91  The big catch with using `a` piped into `foreach` is that values are passed
    92  as strings rather than numbers.
    93  
    94  ### Tips when writing JSON inside for loops
    95  
    96  One of the drawbacks (or maybe advantages, depending on your perspective) of
    97  JSON is that parsers generally expect a complete file for processing in that
    98  the JSON specification requires closing tags for every opening tag. This means
    99  it's not always suitable for streaming. For example
   100  
   101  ```
   102  » ja [1..3] -> foreach i { out ({ "$i": $i }) }
   103  { "1": 1 }
   104  { "2": 2 }
   105  { "3": 3 }
   106  ```
   107  
   108  **What does this even mean and how can you build a JSON file up sequentially?**
   109  
   110  One answer if to write the output in a streaming file format and convert back
   111  to JSON
   112  
   113  ```
   114  » ja [1..3] -> foreach i { out (- "$i": $i) }
   115  - "1": 1
   116  - "2": 2
   117  - "3": 3
   118  
   119  » ja [1..3] -> foreach i { out (- "$i": $i) } -> cast yaml -> format json
   120  [
   121      {
   122          "1": 1
   123      },
   124      {
   125          "2": 2
   126      },
   127      {
   128          "3": 3
   129      }
   130  ]
   131  ```
   132  
   133  **What if I'm returning an object rather than writing one?**
   134  
   135  The problem with building JSON structures from existing structures is that you
   136  can quickly end up with invalid JSON due to the specifications strict use of
   137  commas.
   138  
   139  For example in the code below, each item block is it's own object and there are
   140  no `[ ... ]` encapsulating them to denote it is an array of objects, nor are
   141  the objects terminated by a comma.
   142  
   143  ```
   144  » config -> [ shell ] -> formap k v { $v -> alter /Foo Bar }
   145  {
   146      "Data-Type": "bool",
   147      "Default": true,
   148      "Description": "Display the interactive shell's hint text helper. Please note, even when this is disabled, it will still appear when used for regexp searches and other readline-specific functions",
   149      "Dynamic": false,
   150      "Foo": "Bar",
   151      "Global": true,
   152      "Value": true
   153  }
   154  {
   155      "Data-Type": "block",
   156      "Default": "{ progress $PID }",
   157      "Description": "Murex function to execute when an `exec` process is stopped",
   158      "Dynamic": false,
   159      "Foo": "Bar",
   160      "Global": true,
   161      "Value": "{ progress $PID }"
   162  }
   163  {
   164      "Data-Type": "bool",
   165      "Default": true,
   166      "Description": "ANSI escape sequences in Murex builtins to highlight syntax errors, history completions, {SGR} variables, etc",
   167      "Dynamic": false,
   168      "Foo": "Bar",
   169      "Global": true,
   170      "Value": true
   171  }
   172  ...
   173  ```
   174  
   175  Luckily JSON also has it's own streaming format: JSON lines (`jsonl`). We can
   176  `cast` this output as `jsonl` then `format` it back into valid JSON:
   177  
   178  ```
   179  » config -> [ shell ] -> formap k v { $v -> alter /Foo Bar } -> cast jsonl -> format json
   180  [
   181      {
   182          "Data-Type": "bool",
   183          "Default": true,
   184          "Description": "Write shell history (interactive shell) to disk",
   185          "Dynamic": false,
   186          "Foo": "Bar",
   187          "Global": true,
   188          "Value": true
   189      },
   190      {
   191          "Data-Type": "int",
   192          "Default": 4,
   193          "Description": "Maximum number of lines with auto-completion suggestions to display",
   194          "Dynamic": false,
   195          "Foo": "Bar",
   196          "Global": true,
   197          "Value": "6"
   198      },
   199      {
   200          "Data-Type": "bool",
   201          "Default": true,
   202          "Description": "Display some status information about the stop process when ctrl+z is pressed (conceptually similar to ctrl+t / SIGINFO on some BSDs)",
   203          "Dynamic": false,
   204          "Foo": "Bar",
   205          "Global": true,
   206          "Value": true
   207      },
   208  ...
   209  ```
   210  
   211  #### `foreach` will automatically cast it's output as `jsonl` _if_ it's STDIN type is `json`
   212  
   213  ```
   214  » ja [Tom,Dick,Sally] -> foreach name { out Hello $name }
   215  Hello Tom
   216  Hello Dick
   217  Hello Sally
   218  
   219  » ja [Tom,Dick,Sally] -> foreach name { out Hello $name } -> debug -> [[ /Data-Type/Murex ]]
   220  jsonl
   221  
   222  » ja [Tom,Dick,Sally] -> foreach name { out Hello $name } -> format json
   223  [
   224      "Hello Tom",
   225      "Hello Dick",
   226      "Hello Sally"
   227  ]
   228  ```
   229  
   230  ## See Also
   231  
   232  * [`a` (mkarray)](../commands/a.md):
   233    A sophisticated yet simple way to build an array or list
   234  * [`break`](../commands/break.md):
   235    Terminate execution of a block within your processes scope
   236  * [`foreach`](../commands/foreach.md):
   237    Iterate through an array
   238  * [`formap`](../commands/formap.md):
   239    Iterate through a map or other collection of data
   240  * [`if`](../commands/if.md):
   241    Conditional statement to execute different blocks of code depending on the result of the condition
   242  * [`ja` (mkarray)](../commands/ja.md):
   243    A sophisticated yet simply way to build a JSON array
   244  * [`let`](../commands/let.md):
   245    Evaluate a mathematical function and assign to variable (deprecated)
   246  * [`set`](../commands/set.md):
   247    Define a local variable and set it's value
   248  * [`while`](../commands/while.md):
   249    Loop until condition false
   250  
   251  <hr/>
   252  
   253  This document was generated from [builtins/core/structs/for_doc.yaml](https://github.com/lmorg/murex/blob/master/builtins/core/structs/for_doc.yaml).