operands function works differently inside a procedure - maple

Let's define a procedure
[> f:=proc(s)
s:={1}: {op(s),2};
end proc:
then
[> f('s');
{2, {1}}
but
[> s:={1}: {op(s),2};
{1, 2}
So why do we have a different result?
Using a local variable we can get the expected result though:
[> f:=proc(s) local S;
S:={1}: s:=S; {op(S),2};
end proc:
f('s');
{1, 2}

Your call to the procedure is written to have a side-effect on the uneval-quoted name passed as argument. (Personally I think that is an evil programming practice, and ill consequences are not unexpected.)
Since you have wrapped the name in uneval-quotes, then an extra eval allows you access. Eg,
f:=proc(s)
s:={1}; {op(eval(s)),2};
end proc:
f('s');
{1, 2}

Related

Dealing with more than one Optional parameter

I have a procedure
f:=proc(x,option1,option2) ... end proc;
In my case option1 is always an integer and option2 is either a list or something else (including integer). Both options are optional, so these commands work as expected:
f(x);
f(x,3);
f(x,4,[1,2]);
f(x,5,3);
f(x,6,expand);
But if option1 isn't specified then I don't know an easy way to deal with it since Maple doesn't allow the usage like
f(x,,3);
f(x,,[1,2]);
I can make it understand
f(x,[1,2]);
but I still have a problem with
f(x,3);
since it's not clear if 3 is option1 or option2. I can rewrite the code to understand function calls in this format
f(x,[1,option1],[2,option2]);
f(x,[1,option1]);
f(x,[2,option2]);
but I'm curious if there is a simpler way to achieve that since for some Maple functions (like plot) the order of most options doesn't matter.
As others already mentioned one solution is using "kwarg" (keyword argument). You can also use _passed or _rest. You can read more in Maple help pages or Maple programming guide (https://www.maplesoft.com/documentation_center/Maple2021/ProgrammingGuide.pdf).
Here is just an example how you can use them. _passed is for when you want to say whatever that has been passed to your procedure, and _rest is for whatever that has been passed to your procedure except the ones that are already assigned to the parameters you mentioned inside the parentheses in front of proc. Let's say we want to define a procedure with 1 necessary argument and possible 2 optional arguments. If there are two optional arguments given, we assume the first one is always option1 and the second one is option2, but if only one optional argument is given, then depending on if it is of type integer or not it will be option1 or option2 respectively.
To ask the number of passed or the rest of passed arguments you can use _npassed and _nrest. And the command assigned() checks if something is assigned a value or not. You can check if something is of a specific type, by type(-,-) or - :: -. So here is the simple code.
test := proc( x )
local option1, option2:
if _nrest = 2 then
option1 := _rest[1]:
option2 := _rest[2]:
elif _nrest = 1 then
if _rest[1] :: integer then
option1 := _rest[1]:
else
option2 := _rest[1]:
end if:
end if:
printf( "necessary argument is %a\n", x ):
if assigned( option1 ) then
printf( "option 1 is given and is %d\n", option1 ):
end if:
if assigned( option2 ) then
printf( "option 2 is given and is %a\n", option2 ):
end if:
end proc:
Here is a screenshot of the output of the above procedure for different inputs.
Most of the plotting commands use Maple's more modern argument-processing to manage procedure options.
In particular most options to plotting commands are provided as so-called keyword options. That automatically provides the functionlity in which the location (of such options) doesn't matter.
For example,
f:=proc(v,
{ord::{integer,NoUserValue}:=':-NoUserValue'},
{special::{integer,list,NoUserValue}:=':-NoUserValue'});
print(':-ord'=ord, ':-special'=special);
end proc:
f(x);
ord = NoUserValue, special = NoUserValue
f(x,ord=3);
ord = 3, special = NoUserValue
f(x,special=5);
ord = NoUserValue, special = 5
f(x,special=5,ord=3);
ord = 3, special = 5
f(x,ord=3,special=5);
ord = 3, special = 5
As you've noticed, you [logically] cannot use multiple *positional/ordered" parameters if both have the same type and some earlier ones are missing.
If you really wanted you could make one of those options into a positional parameter, although naturally that would lose its flexibility of arbitrary placement. For example,
restart;
f2:=proc(v,
ord::{integer,NoUserValue}:=':-NoUserValue',
{special::{integer,list,NoUserValue}:=':-NoUserValue'});
print(':-ord'=ord, ':-special'=special);
end proc:
f2(x);
f2(x,3);
f2(x,special=5);
f2(x,special=5,3);
f2(x,3,special=5);
restart;
f3:=proc(v,
special::{integer,list,NoUserValue}:=':-NoUserValue',
{ord::{integer,NoUserValue}:=':-NoUserValue'});
print(':-ord'=ord, ':-special'=special);
end proc:
f3(x);
f3(x,5);
f3(x,ord=3);
f3(x,ord=3,5);
f3(x,5,ord=3);
There are too many variants to show them all here, sensibly.
You don't have to use the name "NoUserValue" as the default values.
Use keyword arguments.
f:=proc(x,{op1::integer:=0,op2::{list,integer}:={}},$)
if has([_passed],'op1') then
print("op1 =",op1);
else
print("op1 not passed");
fi;
if has([_passed],'op2') then
print("op2 =",op2);
else
print("op2 not passed");
fi;
#rest of code
end proc;
Now you can do
f(x,'op2'=[1,2,3])
"op1 not passed"
"op2 =", [1, 2, 3]
And
f(x,'op1'=99)
"op1 =", 99
"op2 not passed"
And
f(x)
"op1 not passed"
"op2 not passed"
And
f(x,'op1'=99,'op2'=[1,2,3])
"op1 =", 99
"op2 =", [1, 2, 3]
And
f(x,'op1'=99,'op2'=19827)
"op1 =", 99
"op2 =", 19827
Make sure to use 'op1'=value when calling, and not op1=value

Check if indexed variable has a value assigned somewhere

Part 1
If we have this
[> restart; a[5]:=23: a[7]:=41:
then
[> about(a); whattype(a);
a:
nothing known about this object
symbol
but
[> print(a);
table([5 = 23, 7 = 41])
and
[> convert(a,list);
[23, 41]
So Maple does have enough information about variable a and its indexes somewhere. How can we check that information without printing?
For small indexes it's not a problem, but if b[123457891234578912345789]:=789; then how do we check if there is some index i for which b[i] has a defined value and what that index i is (without printing, i. e. not manually)?
Part 2
I'd like to be able to work with such variables inside a function and return such variable.
For example, let's say I want to increase all indexes by 1. If I know which indexes exist then I can do that
[> a[5]:=23: a[7]:=41:
f:=proc(x) local y;
y[6]:=x[5]; y[8]:=x[7]; y;
end proc:
b:=f(a); b[6]; b[8];
b:=y
23
41
but often I don't know what indexes variable a has.
There are several programmatic queries you can make,
restart;
a[5]:=23: a[7]:=41:
# test whether a is assigned
assigned('a');
true
# test whether a is assigned a table
type(a, table);
true
# test whether table a has any indexed values
evalb( nops([indices(a, 'nolist')]) > 0 );
true
assigned('a'[7]);
true
If you wish you can make such queries prior to attempting to access and utilize the indexed reference (ie. to check that it has an assigned value). For example,
if type(a,table) and assigned('a'[7]) then
a[7];
end if;
41
if type(a, table) then
[indices(a, 'nolist')];
end if;
[5, 7]

How to properly declare an N-dimensional queue inline in SystemVerilog?

If I have 2D queue of ints, I would expect to be able to declare it inline like so:
int my_queue[$][$] = {{1, 2}, {3, 4}};
I have also seen
typedef int int_queue[$];
int_queue my_queue[$] = {{1, 2}, {3, 4}};
Instead, when I compile, VCS provides me with an Incompatible Complex Type error:
Type of source expression is incompatible with type of target expression.
Mismatching types cannot be used in assignments, initializations and
instantiations. The type of the target is 'int$[$][$]', while the type of
the source is 'bit[63:0]'.
Which implies to me that VCS expects the right hand side of the equation to be cast properly. The way around this that I have been using is:
typedef int int_queue[$];
typedef int_queue int_queue_of_queues[$];
int_queue_of_queues my_queue = int_queue_of_queues'{{1, 2}, {3, 4}};
But this adds N extra typedefs and lines for N dimensions, and I would rather do this in one line. If I had a way to cast the right hand side of the declaration without a typedef, then this would be simple, but I don't know if that is possible.
The array concatenation syntax only works on a single dimension. You cannot nest the {}'s because of cases where there is ambiguity between array {} and integral {} concatenation. You need to use array assignment pattern in the outer, or both dimensions. I prefer using assignment patterns for both dimensions making it clearer that these are array elements, not integral concatenations.
int my_queue[$][$] = '{'{1, 2}, '{3, 4}};
See section 10.10 Unpacked array concatenation in the IEEE 1800-2017
LRM.

Using unquote_splicing in macros with modified lists

Elixir's unquote_splicing works without issues when directly unquoting passed lists. For example, calling the macro below Test1.wrap([1,2,3]) will correctly return [0,0,0,1,2,3,0,0,0].
defmodule Test1 do
defmacro wrap(nums) do
quote do
[0,0,0, unquote_splicing(nums), 0,0,0]
end
end
end
But if I make any changes to the list and then try calling unquote_splicing, Elixir won't even let me define the macro:
defmodule Test2 do
defmacro double_wrap(nums) do
quote do
doubles = Enum.map(unquote(nums), & &1*2)
[0,0,0, unquote_splicing(doubles), 0,0,0]
end
end
end
This will directly raise a compile error:
warning: variable "doubles" does not exist and is being expanded to "doubles()", please use parentheses to remove the ambiguity or change the variable name
iex:37: Test.double_wrap/1
** (CompileError) iex:37: undefined function doubles/0
(elixir) src/elixir_locals.erl:108: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
(elixir) src/elixir_locals.erl:108: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
I have tried a bunch of things so far, such as:
Using nested quotes
Using bind_quoted
Going through Macro and Code docs
but nothing has worked and I can't figure out what I'm doing wrong.
What is returned by a macro, is directly injected in place of the calling code. Kernel.SpecialForms.unquote/1 (as well as unquote_splicing/1) is used to get access to the caller context. That is why your code raises: there is no local variable doubles defined in the caller context.
What you can do, would be to declare doubles outside of the quote block.
defmodule D do
defmacro double_wrap(nums) do
doubles = Enum.map(nums, & &1*2)
quote do
[0,0,0, unquote_splicing(doubles), 0,0,0]
end
end
end
require D
D.double_wrap [1,2,3]
#⇒ [0, 0, 0, 2, 4, 6, 0, 0, 0]
That said, this is happily resolved:
doubles = [1,2,3]
quote do: [0,0,0, unquote_splicing(doubles), 0,0,0]
#⇒ [0, 0, 0, 1, 2, 3, 0, 0, 0]
And this is not, because there is no doubles in the caller context:
quote do
doubles = [1,2,3]
[0,0,0, unquote_splicing(doubles), 0,0,0]
end
#⇒ ☠️ ** (CompileError) iex:7: undefined function doubles/0
The error message says undefined function, because elixir tries a local variable, and if it does not find it in the current context, it attempts to call the function with this name and the arity zero.
There's no need to reach outside the quote block to retrieve the value of doubles when doubles is defined inside the quote block. Variables that are defined inside a quote block automatically have their values embedded in the AST. Therefore, you can use the function List.flatten():
defmodule A do
defmacro double_wrap(nums) do
quote do
doubles = Enum.map(unquote(nums), & &1*2)
List.flatten [0,0,0, doubles, 0,0,0]
end
end
end
In iex:
~/elixir_programs$ iex
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c "a.ex"
[A]
iex(2)> require A
A
iex(3)> A.double_wrap [1, 2, 3]
[0, 0, 0, 2, 4, 6, 0, 0, 0]
iex(4)>

Error, (in addList) invalid subscript selector

I have a list of numbers and I want to add them and then multiply them by a constant.
addList := proc(a::list,b::integer)::integer;
local x,i,s;
description "add a list of numbers and multiply by a constant";
x:=b;
s:=0;
for i in a do
s:=s+a[i];
end do;
s:=s*x;
end proc;
sumList := addList([2, 2, 3], 2) works fine but at the same time sumList := addList([20, 20, 30], 2) gives an error.
Can somebody point out the error ?
In the for loop you do s:=s+a[i] but i is set to one of the values in a already - not the index of a value. A first pass fix would be to just change the statement above to s:=s+i.
You could also write the function as
proc(a::list,b::integer)::integer;
convert(a,`+`)*b;
end;
Even shorter, there is
addList:= (a,b)-> `+`(a[])*b;