< Back to IRCAM Forum

Local tabs

Yesterday I was working on a few macros and found an error that I thought weird. I don’t believe I’ve encountered this before but my memory might be faulty or I might just be out of practice. In the case of a macro, if I try to make a tab local, I will get the following error:

antescofo~: Bad argument 1 in function @insert (got undef): a tab or map is expected [t33]

The macro code is this:

@fun_def midi2symbt($x) {
  @local $y, $h
  $h := []
  forall $y in @size($x) {
    $h := @insert($h, (@size($x) + 1), @hz2symb(@midi2hz($x[$y])))
  }
  return $h
}

What is the solution to make the $h tab local instead of global in similar functions?

Thank you

Hello Mathieul.

I am not sure to understand well the question. The code given is functioning well with version 1.401. For instance, called with argument [62, 86], the returned value is ["D4", "D6"]. Which version of Antescofo are you using?

Notice that @midi2symbt is a function, not a macro (macro-definition begins with @macro_def). Both functions and macros exists in Antescofo. The latter is a syntactic text substitution that occurs at parsing time, without evaluation. A macro-definition has no local variable (the expanded text may represent a group that introduce a local variable, but the macro itself has no local variable). In most case, you should prefer to use a function over a macro: they can be recursive, they can introduce local variables, etc. Macro are really needed only in a few cases, for instance if you want to pass as argument the name of a variable (and not its value). A comparison between macro, function and process is detailed here and here.

Function may have local variables (like function in other programing language). The variable $h is a local variable (it is introduced by the @localkeyword) and it is initialized by a value representing the empty tab. After transformation, the tab is returned as the result of the function application. Are you looking for another behavior?

Your approach (using a forall loop and the @insert function) is not optimal in term of efficiency, because the tab must be extended to accommodate a new element at each iteration. This cost is negligible but we can do better, for example with a tab comprehension:

@fun_def m2($x) {
return [ @hz2symb(@midi2hz($y)) | $y in $x ]
}

Here the tab comprehension is equivalent to a functional map expression (using a lambda to transform each element):

@fun_def m3($x) {
return @map($y.(@hz2symb(@midi2hz($y))), $x)
}

The last two definitions allocate the space needed for the resulting tab in one step. The gain is negligible and can be ignored but the functional style may be more attractive to some programmers.

I realize I was on an older version on this computer. It worked without any issues after updating. It also corrected a few other bizarre bugs. And yes, of course it’s a function not a macro, sorry for the confusion.

And thank you for the efficiency tips. I’ll have look into more of the tab documentation to get into lambda transformations.

Another quick question: within the Antescofo language is it possible to combine strings for variable names like with the @command in the Max world? For example having various variables called $var followed by a number and being able to call these by writing $var+$num. A string2var function could one say.

It is possible to combine strings to forge a variable names but only at the macro-level, see here. They are two already defined macros to do that. You can alo use a macro @concat like this one:


@macro_def @concat($x, $y) { $x$y }

@concat($x,123) := 10

print $x123 

The call to @concat($x,123) is substituted by $x123 and print will receive 10 as argument. However, the substitution is done before any evaluation, when the program is loaded. Which means that the argument of the macro must be a literal integer. For instance

$n := 123
@concat($x,$n) := 11

will raise a warning

Warning: Variable '$x' has never been set  

and an error:

Bad duration (not numeric, get <undef>)

when the program is evaluated because the macro is expanded into

$x $n := 11

and $x is undefined at this point.

Usually, local variables in function and process can be used to avoid the need to ‘compute’ a variable name. You can also use a map. For example:

$x := MAP { 123 -> "a", 124 -> "b", 125 -> "c" }
$y := 123
$z := $x($y)

that is, instead of forging the name, you forge a key and use it to retrieve the corresponding value in a map. In the example, I use the prefix as a variable refering to the map and the suffix as a key to retrieve the value.

Jl.

That was much clearer. I will test it out and play around with it tonight. Thanks a lot for the help