First class environments: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|REXX}}: added the REXX language. -- ~~~~)
m (→‎{{header|REXX}}: Tidy up / shorten by removing unnecessary output)
Line 431: Line 431:


=={{header|REXX}}==
=={{header|REXX}}==
<lang rexx>/*REXX program illustrates first-class environments (using hailstone #s)*/
<lang rexx>
/*REXX program illustrates first-class environments (using hailstone #s)*/


parse arg #envs .; env_.=; if #envs=='' then #envs=12
parse arg #envs .; env_.=; if #envs=='' then #envs=12
Line 469: Line 468:
/*─────────────────────────────────────HAILSTONE (Collatz) subroutine───*/
/*─────────────────────────────────────HAILSTONE (Collatz) subroutine───*/
hailstone: procedure expose env_.; arg n;_=word(env_.n,words(env_.n))
hailstone: procedure expose env_.; arg n;_=word(env_.n,words(env_.n))
if _==1 then return ''; env_.0=0; if _//2==0 then return _%2; return _*3+1
if _==1 then return ''; env_.0=0; if _//2==0 then return _%2; return _*3+1</lang>
{{out}}
</lang>
<pre>
Output when using the default input (<tt> 12 </tt>):
<pre style="height:65ex;overflow:scroll">
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
--- --- --- --- --- --- --- --- --- --- --- ---
--- --- --- --- --- --- --- --- --- --- --- ---
Line 497: Line 495:
=== === === === === === === === === === === ===
=== === === === === === === === === === === ===
0 1 7 2 5 8 16 3 19 6 14 9
0 1 7 2 5 8 16 3 19 6 14 9
</pre>
Output when the input is <tt> 50 </tt>:
<pre style="height:20ex;overflow:scroll">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
1 10 2 16 3 22 4 28 5 34 6 40 7 46 8 52 9 58 10 64 11 70 12 76 13 82 14 88 15 94 16 100 17 106 18 112 19 118 20 124 21 130 22 136 23 142 24 148 25
5 1 8 10 11 2 14 16 17 3 20 22 23 4 26 28 29 5 32 34 35 6 38 40 41 7 44 46 47 8 50 52 53 9 56 58 59 10 62 64 65 11 68 70 71 12 74 76
16 4 5 34 1 7 8 52 10 10 11 70 2 13 14 88 16 16 17 106 3 19 20 124 22 22 23 142 4 25 26 160 28 28 29 178 5 31 32 196 34 34 35 214 6 37 38
8 2 16 17 22 4 26 5 5 34 35 1 40 7 44 8 8 52 53 10 58 10 62 11 11 70 71 2 76 13 80 14 14 88 89 16 94 16 98 17 17 106 107 3 112 19
4 1 8 52 11 2 13 16 16 17 106 20 22 22 4 4 26 160 5 29 5 31 34 34 35 214 1 38 40 40 7 7 44 268 8 47 8 49 52 52 53 322 10 56 58
2 4 26 34 1 40 8 8 52 53 10 11 11 2 2 13 80 16 88 16 94 17 17 106 107 19 20 20 22 22 22 134 4 142 4 148 26 26 160 161 5 28 29
1 2 13 17 20 4 4 26 160 5 34 34 1 1 40 40 8 44 8 47 52 52 53 322 58 10 10 11 11 11 67 2 71 2 74 13 13 80 484 16 14 88
1 40 52 10 2 2 13 80 16 17 17 20 20 4 22 4 142 26 26 160 161 29 5 5 34 34 34 202 1 214 1 37 40 40 40 242 8 7 44
20 26 5 1 1 40 40 8 52 52 10 10 2 11 2 71 13 13 80 484 88 16 16 17 17 17 101 107 112 20 20 20 121 4 22 22
10 13 16 20 20 4 26 26 5 5 1 34 1 214 40 40 40 242 44 8 8 52 52 52 304 322 56 10 10 10 364 2 11 11
5 40 8 10 10 2 13 13 16 16 17 107 20 20 20 121 22 4 4 26 26 26 152 161 28 5 5 5 182 1 34 34
16 20 4 5 5 1 40 40 8 8 52 322 10 10 10 364 11 2 2 13 13 13 76 484 14 16 16 16 91 17 17
8 10 2 16 16 20 20 4 4 26 161 5 5 5 182 34 1 1 40 40 40 38 242 7 8 8 8 274 52 52
4 5 1 8 8 10 10 2 2 13 484 16 16 16 91 17 20 20 20 19 121 22 4 4 4 137 26 26
2 16 4 4 5 5 1 1 40 242 8 8 8 274 52 10 10 10 58 364 11 2 2 2 412 13 13
1 8 2 2 16 16 20 121 4 4 4 137 26 5 5 5 29 182 34 1 1 1 206 40 40
4 1 1 8 8 10 364 2 2 2 412 13 16 16 16 88 91 17 103 20 20
2 4 4 5 182 1 1 1 206 40 8 8 8 44 274 52 310 10 10
1 2 2 16 91 103 20 4 4 4 22 137 26 155 5 5
1 1 8 274 310 10 2 2 2 11 412 13 466 16 16
4 137 155 5 1 1 1 34 206 40 233 8 8
2 412 466 16 17 103 20 700 4 4
1 206 233 8 52 310 10 350 2 2
103 700 4 26 155 5 175 1 1
310 350 2 13 466 16 526
155 175 1 40 233 8 263
466 526 20 700 4 790
233 263 10 350 2 395
700 790 5 175 1 186
350 395 16 526 593
175 186 8 263 780
526 593 4 790 890
263 780 2 395 445
790 890 1 186 336
395 445 593 668
186 336 780 334
593 668 890 167
780 334 445 502
890 167 336 251
445 502 668 754
336 251 334 377
668 754 167 132
334 377 502 566
167 132 251 283
502 566 754 850
251 283 377 425
754 850 132 276
377 425 566 638
132 276 283 319
566 638 850 958
283 319 425 479
850 958 276 438
425 479 638 719
276 438 319 158
638 719 958 079
319 158 479 238
958 079 438 619
479 238 719 858
438 619 158 429
719 858 079 288
158 429 238 644
079 288 619 822
238 644 858 911
619 822 429 734
858 911 288 367
429 734 644 102
288 367 822 051
644 102 911 154
822 051 734 077
911 154 367 232
734 077 102 616
367 232 051 308
102 616 154 154
051 308 077 577
154 154 232 732
077 577 616 866
232 732 308 433
616 866 154 300
308 433 577 650
154 300 732 325
577 650 866 976
732 325 433 488
866 976 300 244
433 488 650 122
300 244 325 61
650 122 976 184
325 61 488 92
976 184 244 46
488 92 122 23
244 46 61 70
122 23 184 35
61 70 92 106
184 35 46 53
92 106 23 160
46 53 70 80
23 160 35 40
70 80 106 20
35 40 53 10
106 20 160 5
53 10 80 16
160 5 40 8
80 16 20 4
40 8 10 2
20 4 5 1
10 2 16
5 1 8
16 4
8 2
4 1
2
1
=== === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === === ===
0 1 7 2 5 8 16 3 19 6 14 9 9 17 17 4 12 20 20 7 7 15 15 10 23 10 111 18 18 18 106 5 26 13 13 21 21 21 34 8 109 8 29 16 16 16 104 11 24 24
</pre>
</pre>



Revision as of 08:06, 3 April 2012

Task
First class environments
You are encouraged to solve this task according to the task description, using any language you may know.

According to Wikipedia, "In computing, a first-class object ... is an entity that can be constructed at run-time, passed as a parameter, returned from a subroutine, or assigned into a variable".

Often this term is used in the context of "first class functions". In an analogous way, a programming language may support "first class environments".

The environment is minimally, the set of variables accessable to a statement being executed. Change the environments and the same statement could produce different results when executed.

Often an environment is captured in a closure, which encapsulates a function together with an environment. That environment, however, is not first-class, as it cannot be created, passed etc. independently from the function's code.

Therefore, a first class environment is a set of variable bindings which can be constructed at run-time, passed as a parameter, returned from a subroutine, or assigned into a variable. It is like a closure without code. A statement must be able to be executed within a stored first class environment and act according to the environment variable values stored within.

The task: Build a dozen environments, and a single piece of code to be run repeatedly in each of these envionments.

Each environment contains the bindings for two variables: A value in the Hailstone sequence, and a count which is incremented until the value drops to 1. The initial hailstone values are 1 through 12, and the count in each environment is zero.

When the code runs, it calculates the next hailstone step in the current environment (unless the value is already 1) and counts the steps. Then it prints the current value in a tabular form.

When all hailstone values dropped to 1, processing stops, and the total number of hailstone steps for each environment is printed.

C

Well, this fits the semantics, not sure about the spirit… <lang C>#include <stdio.h>

  1. define JOBS 12
  2. define jobs(a) for (switch_to(a = 0); a < JOBS || !printf("\n"); switch_to(++a))

typedef struct { int seq, cnt; } env_t;

env_t env[JOBS] = Template:0, 0; int *seq, *cnt;

void hail() { printf("% 4d", *seq); if (*seq == 1) return; ++*cnt; *seq = (*seq & 1) ? 3 * *seq + 1 : *seq / 2; }

void switch_to(int id) { seq = &env[id].seq; cnt = &env[id].cnt; }

int main() { int i; jobs(i) { env[i].seq = i + 1; }

again: jobs(i) { hail(); } jobs(i) { if (1 != *seq) goto again; }

printf("COUNTS:\n"); jobs(i) { printf("% 4d", *cnt); }

return 0; }</lang>

Output:
   1   2   3   4   5   6   7   8   9  10  11  12
   1   1  10   2  16   3  22   4  28   5  34   6
   1   1   5   1   8  10  11   2  14  16  17   3
   1   1  16   1   4   5  34   1   7   8  52  10
   1   1   8   1   2  16  17   1  22   4  26   5
   1   1   4   1   1   8  52   1  11   2  13  16
   1   1   2   1   1   4  26   1  34   1  40   8
   1   1   1   1   1   2  13   1  17   1  20   4
   1   1   1   1   1   1  40   1  52   1  10   2
   1   1   1   1   1   1  20   1  26   1   5   1
   1   1   1   1   1   1  10   1  13   1  16   1
   1   1   1   1   1   1   5   1  40   1   8   1
   1   1   1   1   1   1  16   1  20   1   4   1
   1   1   1   1   1   1   8   1  10   1   2   1
   1   1   1   1   1   1   4   1   5   1   1   1
   1   1   1   1   1   1   2   1  16   1   1   1
   1   1   1   1   1   1   1   1   8   1   1   1
   1   1   1   1   1   1   1   1   4   1   1   1
   1   1   1   1   1   1   1   1   2   1   1   1

COUNTS:
   0   1   7   2   5   8  16   3  19   6  14   9

Go

This example is incorrect. Please fix the code and remove this message.

Details: The libraries have changed and this example no longer works.

Using eval package originally part of Go standard library, now externally hosted and maintained.

Variance: package support is yet incomplete, so the common piece of code has no way to print hv, as requested by the task. Instead, printing is done outside of the common piece of code, by directly inspecting the environments. <lang go>package main

import (

   "bitbucket.org/binet/go-eval/pkg/eval"
   "fmt"
   "go/parser"
   "go/token"

)

type pair struct {

   name  string
   value int

}

type environment struct {

   binding map[string]*intV
   world   *eval.World

}

func newEnvironment(iv []pair) (environment, error) {

   e := environment{make(map[string]*intV), eval.NewWorld()}
   for _, p := range iv {
       v := intV(p.value)
       err := e.world.DefineVar(p.name, eval.IntType, &v)
       if err != nil {
           return environment{}, err
       }
       e.binding[p.name] = &v
   }
   return e, nil

}

func main() {

   // create 12 environments with intial values
   e12 := make([]environment, 12)
   var err error
   for i := range e12 {
       e12[i], err = newEnvironment([]pair{{"hv", i + 1}, {"count", 0}})
       if err != nil {
           fmt.Println(err)
           return
       }
   }
   // define single piece of code.
   // the parser returns a list of abstract syntax trees.
   fset := token.NewFileSet()
   spoc, err := parser.ParseStmtList(fset, "spoc", `
       if hv > 1 {
           if hv % 2 == 0 {
               hv /= 2
           } else {
               hv = 3*hv + 1
           }
           count++
       }
   `)
   if err != nil {
       fmt.Println(err)
       return
   }
   // iterate until all sequences complete

all:

   for {
       // check for all sequences complete
       for i := 0; *e12[i].binding["hv"] == 1; i++ {
           if i == 11 {
               break all
           }
       }
       // iterate sequences
       for _, e := range e12 {
           // output hv in this environment
           // (library deficiency that it can't be done in the spoc.)
           fmt.Printf(" %3d", *e.binding["hv"])
           // bind code,
           code, err := e.world.CompileStmtList(fset, spoc)
           if err != nil {
               fmt.Println(err)
               return
           }
           // evaluate,
           _, err = code.Run()
           if err != nil {
               fmt.Println(err)
               return
           }
           // and abandon the code binding.  it's a little wasteful.
           // code binding could be done up front, once for each environment,
           // and saved in the environment struct.  it's repeated here just
           // to avoid the question of whether it is still a "single piece
           // of code" if it is compiled 12 times and pointers to the 12
           // instances are saved.
       }
       fmt.Println()
   }
   fmt.Println("Counts:")
   for _, e := range e12 {
       fmt.Printf(" %3d", *e.binding["count"])
   }
   fmt.Println()

}

// int value implementation type intV int

func (v *intV) String() string { return fmt.Sprint(*v) } func (v *intV) Get(*eval.Thread) int64 { return int64(*v) } func (v *intV) Set(_ *eval.Thread, x int64) { *v = intV(x) } func (v *intV) Assign(t *eval.Thread, o eval.Value) {

   *v = intV(o.(eval.IntValue).Get(t))

}</lang>

Output:
   1   2   3   4   5   6   7   8   9  10  11  12
   1   1  10   2  16   3  22   4  28   5  34   6
   1   1   5   1   8  10  11   2  14  16  17   3
   1   1  16   1   4   5  34   1   7   8  52  10
   1   1   8   1   2  16  17   1  22   4  26   5
   1   1   4   1   1   8  52   1  11   2  13  16
   1   1   2   1   1   4  26   1  34   1  40   8
   1   1   1   1   1   2  13   1  17   1  20   4
   1   1   1   1   1   1  40   1  52   1  10   2
   1   1   1   1   1   1  20   1  26   1   5   1
   1   1   1   1   1   1  10   1  13   1  16   1
   1   1   1   1   1   1   5   1  40   1   8   1
   1   1   1   1   1   1  16   1  20   1   4   1
   1   1   1   1   1   1   8   1  10   1   2   1
   1   1   1   1   1   1   4   1   5   1   1   1
   1   1   1   1   1   1   2   1  16   1   1   1
   1   1   1   1   1   1   1   1   8   1   1   1
   1   1   1   1   1   1   1   1   4   1   1   1
   1   1   1   1   1   1   1   1   2   1   1   1
Counts:
   0   1   7   2   5   8  16   3  19   6  14   9

Icon and Unicon

The simplest way to create an environment with variables isolated from code in Icon/Unicon is to create instances of records or class objects. <lang Icon>link printf

procedure main()

  every put(environment := [], hailenv(1 to 12,0))  # setup environments 
  printf("Sequences:\n")
  while (e := !environment).sequence > 1 do {
     every hailstep(!environment) 
     printf("\n")
     }
  printf("\nCounts:\n")
  every printf("%4d ",(!environment).count)
  printf("\n")

end

record hailenv(sequence,count)

procedure hailstep(env)

  printf("%4d ",env.sequence)
   if env.sequence ~= 1 then {
       env.count +:= 1
       if env.sequence % 2 = 0 then env.sequence /:= 2 
       else env.sequence := 3 * env.sequence + 1
       }

end</lang>

printf.icn provides formatting

Output:
Sequences:
   1    2    3    4    5    6    7    8    9   10   11   12 
   1    1   10    2   16    3   22    4   28    5   34    6 
   1    1    5    1    8   10   11    2   14   16   17    3 
   1    1   16    1    4    5   34    1    7    8   52   10 
   1    1    8    1    2   16   17    1   22    4   26    5 
   1    1    4    1    1    8   52    1   11    2   13   16 
   1    1    2    1    1    4   26    1   34    1   40    8 
   1    1    1    1    1    2   13    1   17    1   20    4 
   1    1    1    1    1    1   40    1   52    1   10    2 
   1    1    1    1    1    1   20    1   26    1    5    1 
   1    1    1    1    1    1   10    1   13    1   16    1 
   1    1    1    1    1    1    5    1   40    1    8    1 
   1    1    1    1    1    1   16    1   20    1    4    1 
   1    1    1    1    1    1    8    1   10    1    2    1 
   1    1    1    1    1    1    4    1    5    1    1    1 
   1    1    1    1    1    1    2    1   16    1    1    1 
   1    1    1    1    1    1    1    1    8    1    1    1 
   1    1    1    1    1    1    1    1    4    1    1    1 
   1    1    1    1    1    1    1    1    2    1    1    1 

Counts:
   0    1    7    2    5    8   16    3   19    6   14    9 

J

I have tried to determine what makes this task interesting (see talk page), but I am still confused.

Here is my current interpretation of the task requirements: <lang j>coclass 'hailstone'

step=:3 :0

 NB. and determine next element in hailstone sequence
 if.1=N do. N return.end.
   NB. count how many times this has run when N was not 1
   STEP=:STEP+1
 if.0=2|N do.
   N=: N%2
 else.
   N=: 1 + 3*N
 end.

)

create=:3 :0

 STEP=: 0
 N=: y

)

current=:3 :0

 N__y

)

run1=:3 :0

 step__y
 STEP__y

)

run=:3 :0

 old=: 
 while. -. old -: state=: run1"0 y do.
   smoutput 4j0 ": current"0 y
   old=: state
 end.

)</lang>

Example use:

<lang j> environments=: conew&'hailstone'"0 (1+i.12)

  run_hailstone_ environments
  1   1  10   2  16   3  22   4  28   5  34   6
  1   1   5   1   8  10  11   2  14  16  17   3
  1   1  16   1   4   5  34   1   7   8  52  10
  1   1   8   1   2  16  17   1  22   4  26   5
  1   1   4   1   1   8  52   1  11   2  13  16
  1   1   2   1   1   4  26   1  34   1  40   8
  1   1   1   1   1   2  13   1  17   1  20   4
  1   1   1   1   1   1  40   1  52   1  10   2
  1   1   1   1   1   1  20   1  26   1   5   1
  1   1   1   1   1   1  10   1  13   1  16   1
  1   1   1   1   1   1   5   1  40   1   8   1
  1   1   1   1   1   1  16   1  20   1   4   1
  1   1   1   1   1   1   8   1  10   1   2   1
  1   1   1   1   1   1   4   1   5   1   1   1
  1   1   1   1   1   1   2   1  16   1   1   1
  1   1   1   1   1   1   1   1   8   1   1   1
  1   1   1   1   1   1   1   1   4   1   1   1
  1   1   1   1   1   1   1   1   2   1   1   1
  1   1   1   1   1   1   1   1   1   1   1   1

0 1 7 2 5 8 16 3 19 6 14 9</lang> In essence: run is a static method of the class hailstone which, given a list of objects of the class runs all of them until their hailstone sequence number stops changing. It also displays the hailstone sequence number from each of the objects at each step. Its result is the step count from each object.

PicoLisp

Runtime environments can be controlled with the 'job' function: <lang PicoLisp>(let Envs

  (mapcar
     '((N) (list (cons 'N N) (cons 'Cnt 0)))  # Build environments
     (range 1 12) )
  (while (find '((E) (job E (> N 1))) Envs)   # Until all values are 1:
     (for E Envs
        (job E                                # Use environment 'E'
           (prin (align 4 N))
           (unless (= 1 N)
              (inc 'Cnt)                      # Increment step count
              (setq N
                 (if (bit? 1 N)               # Calculate next hailstone value
                    (inc (* N 3))
                    (/ N 2) ) ) ) ) )
     (prinl) )
  (prinl (need 48 '=))
  (for E Envs                                 # For each environment 'E'
     (job E
        (prin (align 4 Cnt)) ) )              # print the step count
  (prinl) )</lang>
Output:
   1   2   3   4   5   6   7   8   9  10  11  12
   1   1  10   2  16   3  22   4  28   5  34   6
   1   1   5   1   8  10  11   2  14  16  17   3
   1   1  16   1   4   5  34   1   7   8  52  10
   1   1   8   1   2  16  17   1  22   4  26   5
   1   1   4   1   1   8  52   1  11   2  13  16
   1   1   2   1   1   4  26   1  34   1  40   8
   1   1   1   1   1   2  13   1  17   1  20   4
   1   1   1   1   1   1  40   1  52   1  10   2
   1   1   1   1   1   1  20   1  26   1   5   1
   1   1   1   1   1   1  10   1  13   1  16   1
   1   1   1   1   1   1   5   1  40   1   8   1
   1   1   1   1   1   1  16   1  20   1   4   1
   1   1   1   1   1   1   8   1  10   1   2   1
   1   1   1   1   1   1   4   1   5   1   1   1
   1   1   1   1   1   1   2   1  16   1   1   1
   1   1   1   1   1   1   1   1   8   1   1   1
   1   1   1   1   1   1   1   1   4   1   1   1
   1   1   1   1   1   1   1   1   2   1   1   1
================================================
   0   1   7   2   5   8  16   3  19   6  14   9

Python

In Python, name bindings are held in dicts, one for global scope and another for local scope. When exec'ing code, you are allowed to give your own dictionaries for these scopes. In this example, two names are held in dictionaries that are used as the local scope for the evaluation of source. <lang python>environments = [{'cnt':0, 'seq':i+1} for i in range(12)]

code = print('% 4d' % seq, end=) if seq != 1:

   cnt += 1
   seq = 3 * seq + 1 if seq & 1 else seq // 2

while any(env['seq'] > 1 for env in environments):

   for env in environments:
       exec(code, globals(), env)
   print()

print('Counts') for env in environments:

   print('% 4d' % env['cnt'], end=)

print()</lang>

Output:
   1   2   3   4   5   6   7   8   9  10  11  12
   1   1  10   2  16   3  22   4  28   5  34   6
   1   1   5   1   8  10  11   2  14  16  17   3
   1   1  16   1   4   5  34   1   7   8  52  10
   1   1   8   1   2  16  17   1  22   4  26   5
   1   1   4   1   1   8  52   1  11   2  13  16
   1   1   2   1   1   4  26   1  34   1  40   8
   1   1   1   1   1   2  13   1  17   1  20   4
   1   1   1   1   1   1  40   1  52   1  10   2
   1   1   1   1   1   1  20   1  26   1   5   1
   1   1   1   1   1   1  10   1  13   1  16   1
   1   1   1   1   1   1   5   1  40   1   8   1
   1   1   1   1   1   1  16   1  20   1   4   1
   1   1   1   1   1   1   8   1  10   1   2   1
   1   1   1   1   1   1   4   1   5   1   1   1
   1   1   1   1   1   1   2   1  16   1   1   1
   1   1   1   1   1   1   1   1   8   1   1   1
   1   1   1   1   1   1   1   1   4   1   1   1
   1   1   1   1   1   1   1   1   2   1   1   1
Counts
   0   1   7   2   5   8  16   3  19   6  14   9

REXX

<lang rexx>/*REXX program illustrates first-class environments (using hailstone #s)*/

parse arg #envs .; env_.=; if #envs== then #envs=12

/*─────────────────────────────────────initialize (twelve) environments.*/

 do init=1 for #envs;  env_.init=init;  end

/*─────────────────────────────────────process environments until done. */

    do forever until env_.0; env_.0=1
         do k=1 for #envs
         env_.k=env_.k hailstone(k)   /*where the rubber meets the road*/
         end   /*k*/
    end        /*forever*/

/*─────────────────────────────────────show results in tabular form. */ count=0

 do lines=-1 until _==; _=
         do j=1 for #envs
               select
               when count== 1 then _=_ right(words(env_.j)-1,3)
               when lines==-1 then _=_ right(j,3)
               when lines== 0 then _=_ right(,3,'-')
               otherwise       _=_ right(word(env_.j,lines),3)
               end   /*select*/
         end       /*j*/
 if count==1 then count=2
 _=strip(_,'T')
 if _== then count=count+1
 if count==1 then _=copies(' ===',#envs)
 if _\== then say substr(_,2)
 end              /*lines*/

exit

/*─────────────────────────────────────HAILSTONE (Collatz) subroutine───*/ hailstone: procedure expose env_.; arg n;_=word(env_.n,words(env_.n)) if _==1 then return ; env_.0=0; if _//2==0 then return _%2; return _*3+1</lang>

Output:
  1   2   3   4   5   6   7   8   9  10  11  12
--- --- --- --- --- --- --- --- --- --- --- ---
  1   2   3   4   5   6   7   8   9  10  11  12
      1  10   2  16   3  22   4  28   5  34   6
          5   1   8  10  11   2  14  16  17   3
         16       4   5  34   1   7   8  52  10
          8       2  16  17      22   4  26   5
          4       1   8  52      11   2  13  16
          2           4  26      34   1  40   8
          1           2  13      17      20   4
                      1  40      52      10   2
                         20      26       5   1
                         10      13      16
                          5      40       8
                         16      20       4
                          8      10       2
                          4       5       1
                          2      16
                          1       8
                                  4
                                  2
                                  1
=== === === === === === === === === === === ===
  0   1   7   2   5   8  16   3  19   6  14   9

Ruby

Translation of: PicoLisp

The object is an environment for instance variables. These variables use the @ sigil. We create 12 objects, and put @n and @cnt inside these objects. We use Object#instance_eval to switch the current object and bring those instance variables into scope. <lang ruby># Build environments envs = (1..12).map do |n|

 Object.new.instance_eval {@n = n; @cnt = 0; self}

end

  1. Until all values are 1:

while envs.find {|e| e.instance_eval {@n} > 1}

 envs.each do |e|
   e.instance_eval do          # Use environment _e_
     printf "%4s", @n
     unless 1 == @n
       @cnt += 1               # Increment step count
       @n = if 1 & @n == 1     # Calculate next hailstone value
              @n * 3 + 1
            else
              @n / 2
            end
     end
   end
 end
 puts

end puts '=' * 48 envs.each do |e| # For each environment _e_

 e.instance_eval do
   printf "%4s", @cnt          # print the step count
 end

end puts</lang> Ruby also provides the binding, an environment for local variables. The problem is that local variables have lexical scope. Ruby needs the lexical scope to parse Ruby code. So, the only way to use a binding is to evaluate a string of Ruby code. We use Kernel#binding to create the bindings, and Kernel#eval to evaluate strings in these bindings. The lines between <<-'eos' and eos are multi-line string literals. <lang ruby># Build environments envs = (1..12).map do |n|

 e = class Object
       # This is a new lexical scope with no local variables.
       # Create a new binding here.
       binding
     end
 eval(<<-'eos', e).call(n)
   n, cnt = nil, 0
   proc {|arg| n = arg}
 eos
 next e

end

  1. Until all values are 1:

while envs.find {|e| eval('n > 1', e)}

 envs.each do |e|
   eval(<<-'eos', e)           # Use environment _e_
     printf "%4s", n
     unless 1 == n
       cnt += 1                # Increment step count
       n = if 1 & n == 1       # Calculate next hailstone value
             n * 3 + 1
           else
             n / 2
           end
     end
   eos
 end
 puts

end puts '=' * 48 envs.each do |e| # For each environment _e_

 eval(<<-'eos', e)
   printf "%4s", cnt           # print the step count
 eos

end puts</lang>

Tcl

The simplest way to make a first-class environment in Tcl is to use a dictionary; the dict with command (and dict update; not shown here) will expand a dictionary and bind it to variables for the duration of its body script. <lang tcl>package require Tcl 8.5

for {set i 1} {$i <= 12} {incr i} {

   dict set hailenv hail$i [dict create num $i steps 0]

} while 1 {

   set loopagain false
   foreach k [dict keys $hailenv] {

dict with hailenv $k { puts -nonewline [format %4d $num] if {$num == 1} { continue } elseif {$num & 1} { set num [expr {3*$num + 1}] } else { set num [expr {$num / 2}] } set loopagain true incr steps }

   }
   puts ""
   if {!$loopagain} break

} puts "Counts..." foreach k [dict keys $hailenv] {

   dict with hailenv $k {

puts -nonewline [format %4d $steps]

   }

} puts ""</lang>

Output:
   1   2   3   4   5   6   7   8   9  10  11  12
   1   1  10   2  16   3  22   4  28   5  34   6
   1   1   5   1   8  10  11   2  14  16  17   3
   1   1  16   1   4   5  34   1   7   8  52  10
   1   1   8   1   2  16  17   1  22   4  26   5
   1   1   4   1   1   8  52   1  11   2  13  16
   1   1   2   1   1   4  26   1  34   1  40   8
   1   1   1   1   1   2  13   1  17   1  20   4
   1   1   1   1   1   1  40   1  52   1  10   2
   1   1   1   1   1   1  20   1  26   1   5   1
   1   1   1   1   1   1  10   1  13   1  16   1
   1   1   1   1   1   1   5   1  40   1   8   1
   1   1   1   1   1   1  16   1  20   1   4   1
   1   1   1   1   1   1   8   1  10   1   2   1
   1   1   1   1   1   1   4   1   5   1   1   1
   1   1   1   1   1   1   2   1  16   1   1   1
   1   1   1   1   1   1   1   1   8   1   1   1
   1   1   1   1   1   1   1   1   4   1   1   1
   1   1   1   1   1   1   1   1   2   1   1   1
   1   1   1   1   1   1   1   1   1   1   1   1
Counts...
   0   1   7   2   5   8  16   3  19   6  14   9