Dynamic variable names: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Tcl}}: example of dereferencing dynamic var name)
(add Ruby)
Line 122: Line 122:
>>> X
>>> X
42</lang>
42</lang>

=={{header|Ruby}}==
<pre>irb(main):001:0> varname = gets
foo
=> "foo\n"
irb(main):002:0> varname.chomp!
=> "foo"
irb(main):003:0> eval "#{varname} = 42"
=> 42
irb(main):004:0> foo
=> 42
irb(main):005:0> puts "the value of #{varname} is #{eval varname}"
the value of foo is 42
=> nil</pre>

However, the dynamic variable is bound to the current context. Consider:
<pre>irb(main):006:0> def deref(v)
irb(main):007:1> eval v
irb(main):008:1> end
=> nil
irb(main):009:0> deref varname
NameError: undefined local variable or method `foo' for main:Object
from (irb):7:in `deref'
from (irb):9:in `eval'
from (irb):7:in `deref'
from (irb):9
from :0</pre>

Ruby lets you pass around the context as an object though:
<pre>irb(main):010:0> binding
=> #<Binding:0x7ff7d954>
irb(main):011:0> def deref(v,b)
irb(main):012:1> eval v, b
irb(main):013:1> end
=> nil
irb(main):014:0> deref varname, binding
=> 42
irb(main):015:0> puts "the value of #{varname} is #{deref varname, binding}"
the value of foo is 42
=> nil</pre>


=={{header|Slate}}==
=={{header|Slate}}==

Revision as of 12:31, 17 June 2009

Task
Dynamic variable names
You are encouraged to solve this task according to the task description, using any language you may know.

Create a variable with a user-defined name. The variable name should not be written in the program text, but should be taken from the user dynamically.

AutoHotkey

<lang AutoHotkey> InputBox, Dynamic, Variable Name %Dynamic% = hello ListVars MsgBox % %dynamic%  ; says hello </lang>

BASIC

Works with: Beta BASIC version 3.0

,

Works with: SAM BASIC
10 INPUT "Enter a variable name", v$
20 KEYIN "LET "+v$+"=42"

Common Lisp

In Common Lisp, symbol objects name variables; symbols are produced from strings by way of read (general syntax) or intern (specificially retrieving or making a symbol).

Symbols are grouped into packages — roughly namespaces — and any time symbols are created at runtime it is usually good to explicitly specify what package they are created in, outside of user/developer tools for working from the REPL (interactive mode) where the current package *package* is appropriate.

Within the standard, every variable is either lexical or special (dynamic scope). There is no global lexical environment, so in order to "create a variable", we must either create our own mechanism to remember it for lexical binding in a later evaluation, or create a special variable. It is unspecified what happens when a symbol not lexically bound or declared special is used as a variable.

Every symbol has a value slot — a field which, roughly, contains its current value considered as a special variable.

Therefore, there are two parts to dynamically creating a variable: we must declare it special, and give it a value. The first part is accomplished by the proclaim function for making declarations at run-time. The second part is simply assigning to the value slot.

<lang lisp>(defun rc-create-variable (name initial-value)

 "Create a global variable whose name is NAME in the current package and which is bound to INITIAL-VALUE."
 (let ((symbol (intern name)))
   (proclaim `(special ,symbol))
   (setf (symbol-value symbol) initial-value)
   symbol))</lang>

<lang lisp> CL-USER> (rc-create-variable "GREETING" "hello") GREETING

CL-USER> (print greeting) "hello"</lang>

Things to note:

  • Once a symbol has been declared special, it cannot be used as a lexical variable. Because of this potentially-surprising behavior, it is conventional to give all symbols naming special variables distinguished names, typically by asterisks as in *greeting*, so that lexical variables will not accidentally be given those names.
  • Some implementations do, to some extent, support global non-special variables; in these, because of the preceding problem, it is better to simply set the value slot and not proclaim it special. However, this may provoke undefined-variable warnings since the compiler or interpreter has no information with which to know the symbol is intended to be a variable.
  • Common Lisp, by default, is case-insensitive; however it accomplishes this by canonicalizing read input to uppercase; there is syntax to denote a lower or mixed-case symbol name, |Foo| or F\o\o. intern does not go through the input path (reader), so we must provide the name in uppercase to make an "ordinary" variable name.

E

In E, there are no global variables, and there is no modification of the local (lexical) environment. However, it is possible to construct a program which binds any given variable name.

<lang e>def makeNounExpr := <elang:evm.makeNounExpr>

def dynVarName(name) {

   def variable := makeNounExpr(null, name, null)
   return e`{
   
       def a := 1
       def b := 2
       def c := 3
   
       {
           def $variable := "BOO!"
           [a, b, c]
       }
   
   }`.eval(safeScope)

}

? dynVarName("foo")

  1. value: [1, 2, 3]

? dynVarName("b")

  1. value: [1, "BOO!", 3]

? dynVarName("c")

  1. value: [1, 2, "BOO!"]</lang>

It is also possible to capture the environment object resulting from the evaluation of the constructed program and use it later; this is done by bindX in Eval in environment#E (except for the program being constant, which is independent).

Forth

<lang forth>s" VARIABLE " pad swap move ." Variable name: " pad 9 + 80 accept pad swap 9 + evaluate</lang> Of course, it is easier for the user to simply type VARIABLE name at the Forth console.

Perl

<lang perl>print "Enter a variable name: "; $varname = <STDIN>; # type in "foo" on standard input chomp($varname); $$varname = 42; # when you try to dereference a string, it will be

               # treated as a "symbolic reference", where they
               # take the string as the name of the variable

print "$foo\n"; # prints "42"</lang>

PHP

<lang php><?php $varname = rtrim(fgets(STDIN)); # type in "foo" on standard input $$varname = 42; echo "$foo\n"; # prints "42" ?></lang>

Python

Works with: Python version 2.x

<lang python>>>> n = raw_input("Enter a variable name: ") Enter a variable name: X >>> exec n + " = 42" >>> X 42</lang>

Works with: Python version 3.x

<lang python>>>> n = input("Enter a variable name: ") Enter a variable name: X >>> exec(n + " = 42") >>> X 42</lang>

Ruby

irb(main):001:0> varname = gets
foo
=> "foo\n"
irb(main):002:0> varname.chomp!
=> "foo"
irb(main):003:0> eval "#{varname} = 42"
=> 42
irb(main):004:0> foo
=> 42
irb(main):005:0> puts "the value of #{varname} is #{eval varname}"
the value of foo is 42
=> nil

However, the dynamic variable is bound to the current context. Consider:

irb(main):006:0> def deref(v)
irb(main):007:1> eval v
irb(main):008:1> end
=> nil
irb(main):009:0> deref varname
NameError: undefined local variable or method `foo' for main:Object
        from (irb):7:in `deref'
        from (irb):9:in `eval'
        from (irb):7:in `deref'
        from (irb):9
        from :0

Ruby lets you pass around the context as an object though:

irb(main):010:0> binding
=> #<Binding:0x7ff7d954>
irb(main):011:0> def deref(v,b)
irb(main):012:1> eval v, b
irb(main):013:1> end
=> nil
irb(main):014:0> deref varname, binding
=> 42
irb(main):015:0> puts "the value of #{varname} is #{deref varname, binding}"
the value of foo is 42
=> nil

Slate

Slate symbols are objects that name methods and slots. "Variable definition" is like defining a method which holds the value of a slot, and "variable access" is just method-call to get that value back. <lang slate> define: #name -> (query: 'Enter a variable name: ') intern. "X" define: name -> 42. X print. </lang>

Tcl

<lang Tcl>puts "Enter a variable name:" gets stdin varname set $varname 42 puts "I have set variable $varname to [set $varname]" </lang> Note that it is more normal to use the user's name to index into a Tcl associative array, as the syntax gets easier to work with in that case: <lang tcl>puts -nonewline "Enter an element name: "; flush stdout gets stdin elemname set ary($elemname) [expr int(rand()*100)] puts "I have set element $elemname to $ary($elemname)"</lang>