First class environments

From Rosetta Code
Revision as of 21:00, 1 July 2011 by Sonia (talk | contribs) (Go solution)
First class environments is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

A first class environment is a set of variable bindings which can be stored in variables, passed to functions, compared, etc.

The task is to 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<lang>

  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</lang>

Go

Works with: weekly.2011-06-23

Using the exp/eval package. Exp packages are experimental, meaning this RC solution is likely just a sketch for something better that will be possible in the future as Go evolves.

Variance: exp/eval (being an incomplete experiment) does not handle packages, 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 (

   "exp/eval"
   "fmt"
   "go/parser"
   "go/token"
   "os"

)

type pair struct {

   name  string
   value int

}

type environment struct {

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

}

func newEnvironment(iv []pair) (environment, os.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 os.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. (you'd think this should be exported // from exp/eval, but then the exp does stand for experimental.) 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

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