Avoid creating temporary scalars when returning multiple arrays - raku

Is it possible to avoid creating temporary scalars when returning multiple arrays from a function:
use v6;
sub func() {
my #a = 1..3;
my #b = 5..10;
return #a, #b;
}
my ($x, $y) = func();
my #x := $x;
my #y := $y;
say "x: ", #x; # OUTPUT: x: [1 2 3]
say "y: ", #y; # OUTPUT: y: [5 6 7 8 9 10]
I would like to avoid creating the temporary variables $x and $y.
Note: It is not possible to replace the function call with
my (#x, #y) = func()
since assignment of a list to an Array is eager and therefore both the returned arrays end up in #x.

Not either of:
my ($x, $y) = func();
my (#x, #y) = func();
But instead either of:
my (#x, #y) := func();
my ($x, $y) := func();
Use # to signal to P6 that, when it needs to distinguish whether something is singular -- "a single array" -- or plural -- "items contained in a single array" -- it should be treated as plural.
Use $ to signal the other way around -- it should be treated as singular.
You can always later explicitly reverse this by doing $#x -- to signal P6 should use the singular perspective for something you originally declared as plural -- or #$x to signal reversing the other way around.
For an analogy, think of a cake cut into several pieces. Is it a single thing or a bunch of pieces? Note also that # caches indexing of the pieces whereas $ just remembers that it's a cake. For large lists of things this can make a big difference.

Related

How to divide every element in array by a certain number?

I have an array:
my #e = <60.922 20.946 8.721 7.292 4.306 2.821 2.765 2.752 2.741 2.725>
I would like to divide every element in the array by the minimum, however
#e /= #e.min
produced a single element, which isn't correct.
I've read https://docs.raku.org/type/Array but I don't understand the basic elements of Raku for this.
How can I divide every item in the array by the same number?
You can use the raku hyper & compound metaoperators like this:
#a >>/=>> #a.min
>>X>> means "apply operator X over all list items (with more items on the left)"
/= means "assign result of divide operator / to replace each original left hand item"
Use / instead of /= if you want to return the list of results but leave #a unchanged functional programming style.
[Edited according to #lizmat & #Sebastian comments]
my #a = 2, 3, 5, 7, 10;
my $div = #a.min;
$_ /= $div for #a;
say #a; # [1 1.5 2.5 3.5 5]
When you iterate over an array, you get mutable elements.
put #e.map( * / #e.min );
OR
put #e.map: * / #e.min;
Sample Input:
my #e = <60.922 20.946 8.721 7.292 4.306 2.821 2.765 2.752 2.741 2.725>;
Sample Output:
22.356697 7.686606 3.200367 2.675963 1.580183 1.035229 1.014679 1.009908 1.005872 1
If you want to continue working with the resultant values, assign the output to a new variable. Or overwrite the original #e array using the .= "back-assignment" operator [ short for #e = #e.map( โ€ฆ ) ]. In the Raku REPL:
~$ raku
Welcome to ๐‘๐š๐ค๐ฎ๐๐จโ„ข v2021.06.
Implementing the ๐‘๐š๐ค๐ฎโ„ข programming language v6.d.
Built on MoarVM version 2021.06.
To exit type 'exit' or '^D'
> my #e = <60.922 20.946 8.721 7.292 4.306 2.821 2.765 2.752 2.741 2.725>;
[60.922 20.946 8.721 7.292 4.306 2.821 2.765 2.752 2.741 2.725]
> #e .= map( * / #e.min );
[22.356697 7.686606 3.200367 2.675963 1.580183 1.035229 1.014679 1.009908 1.005872 1]
> put #e;
22.356697 7.686606 3.200367 2.675963 1.580183 1.035229 1.014679 1.009908 1.005872 1
>

Raku pop() order of execution

Isn't order of execution generally from left to right in Raku?
my #a = my #b = [9 , 3];
say (#a[1] - #a[0]) == (#b[1] R- #b[0]); # False {as expected}
say (#a.pop() - #a.pop()) == (#b.pop() R- #b.pop()); # True {Huh?!?}
This is what I get in Rakudo(tm) v2020.12 and 2021.07.
The first 2 lines make sense, but the third I can not fathom.
It is.
But you should realize that the minus infix operator is just a subroutine under the hood, taking 2 parameters that are evaluated left to right. So when you're saying:
$a - $b
you are in fact calling the infix:<-> sub:
infix:<->($a,$b);
The R meta-operator basically creates a wrap around the infix:<-> sub that reverses the arguments:
my &infix:<R->($a,$b) = &infix:<->.wrap: -> $a, $b { nextwith $b, $a }
So, if you do a:
$a R- $b
you are in fact doing a:
infix:<R->($a,$b)
which is then basically a:
infix:<->($b,$a)
Note that in the call to infix:<R-> in your example, $a become 3, and $b becomes 9 because the order of the arguments is processed left to right. This then calls infix:<->(3,9), producing the -6 value that you would also get without the R.
It may be a little counter-intuitive, but I consider this behaviour as correct. Although the documentation could probably use some additional explanation on this behaviour.
Let me emulate what I assumed was happening in line 3 of my code prefaced with #a is the same as #b is 9, 3 (big number then little number)
(#a.pop() - #a.pop()) == (#b.pop() R- #b.pop())
(3 - 9) == (3 R- 9)
( -6 ) == ( 6 )
False
...That was my expectation. But what raku seems to be doing is
(#a.pop() - #a.pop()) == (#b.pop() R- #b.pop())
#R meta-op swaps 1st `#b.pop()` with 2nd `#b.pop()`
(#a.pop() - #a.pop()) == (#b.pop() - #b.pop())
(3 - 9) == (3 - 9)
( -6 ) == ( -6 )
True
The R in R- swaps functions first, then calls the for values. Since they are the same function, the R in R- has no practical effect.
Side Note: In fuctional programming a 'pure' function will return the same value every time you call it with the same parameters. But pop is not 'pure'. Every call can produce different results. It needs to be used with care.
The R meta op not only reverses the operator, it will also reverse the order in which the operands will be evaluated.
sub term:<a> { say 'a'; '3' }
sub term:<b> { say 'b'; '9' }
say a ~ b;
a
b
ab
Note that a happened first.
If we use R, then b happens first instead.
say a R~ b;
b
a
ba
The problem is that in your code all of the pop calls are getting their data from the same source.
my #data = < a b a b >;
sub term:<l> { my $v = #data.shift; say "l=$v"; return $v }
sub term:<r> { my $v = #data.shift; say "r=$v"; return $v }
say l ~ r;
l=a
r=b
ab
say l R~ r;
r=a
l=b
ab
A way to get around that is to use the reduce meta operator with a list
[-](#a.pop, #a.pop) == [R-](#a.pop, #a.pop)
Or in some other way make sure the pop operations happen in the order you expect.
You could also just use the values directly from the array without using pop.
[-]( #a[0,1] ) == [R-]( #a[2,3] )
Let me emulate what happens by writing the logic one way for #a then manually reversing the operands for #b instead of using R:
my #a = my #b = [9 , 3];
sub apop { #a.pop }
sub bpop { #b.pop }
say apop - apop; # -6
say bpop - bpop; # -6 (operands *manually* reversed)
This not only appeals to my sense of intuition about what's going on, I'm thus far confused why you were confused and why Liz has said "It may be a little counter-intuitive" and you've said it is plain unintuitive!

Destructuring a list with equations in maxima

Say that I have the following list of equations:
list: [x=1, y=2, z=3];
I use this pattern often to have multiple return values from a function. Kind of of like how you would use an object, in for example, javascript. However, in javascript, I can do things like this. Say that myFunction() returns the object {x:1, y:2, z:3}, then I can destructure it with this syntax:
let {x,y,z} = myFunction();
And now x,y,z are assigned the values 1,2,3 in the current scope.
Is there anything like this in maxima? Now I use this:
x: subst(list, x);
y: subst(list, y);
z: subst(list, z);
How about this. Let l be a list of equations of the form somesymbol = somevalue. I think all you need is:
map (lhs, l) :: map (rhs, l);
Here map(lhs, l) yields the list of symbols, and map(rhs, l) yields the list of values. The operator :: means evaluate the left-hand side and assign the right-hand side to it. When the left-hand side is a list, then Maxima assigns each value on the right-hand side to the corresponding element on the left.
E.g.:
(%i1) l : [a = 12, b = 34, d = 56] $
(%i2) map (lhs, l) :: map (rhs, l);
(%o2) [12, 34, 56]
(%i3) values;
(%o3) [l, a, b, d]
(%i4) a;
(%o4) 12
(%i5) b;
(%o5) 34
(%i6) d;
(%o6) 56
You can probably achieve it and write a function that could be called as f(['x, 'y, 'z], list); but you will have to be able to make some assignments between symbols and values. This could be done by writing a tiny ad hoc Lisp function being:
(defun $assign (symb val) (set symb val))
You can see how it works (as a first test) by first typing (form within Maxima):
:lisp (defun $assign (symb val) (set symb val))
Then, use it as: assign('x, 42) which should assign the value 42 to the Maxima variable x.
If you want to go with that idea, you should write a tiny Lisp file in your ~/.maxima directory (this is a directory where you can put your most used functions); call it for instance myfuncs.lisp and put the function above (without the :lisp prefix); then edit (in the very same directory) your maxima-init.mac file, which is read at startup and add the two following things:
add a line containing load("myfuncs.lisp"); before the following part;
define your own Maxima function (in plain Maxima syntax with no need to care about Lisp). Your function should contain some kind of loop for performing all assignments; now you could use the assign(symbol, value) function for each variable.
Your function could be something like:
f(vars, l) := for i:1 thru length(l) do assign(vars[i], l[i]) $
which merely assign each value from the second argument to the corresponding symbol in the first argument.
Thus, f(['x, 'y], [1, 2]) will perform the expected assigments; of course you can start from that for doing more precisely what you need.

Print something completely different when the LP is infeasible in MathProg

Iโ€™m using MathProg (a language specific to the GLPK library, resembling a subset of AMPL) to find topological ranking of vertices of a graph. Itโ€™s an assignment for my linear programming class. Itโ€™s an introductory exercise to make sure we can formulate a simple linear program and solve it using GLPK.
Iโ€™ve written a Perl script that generates the linear program in MathProg for a given graph. It prints values of the variables (ranks of vertices) via printf. If itโ€™s feasible, thatโ€™s exactly what I want; otherwise it prints all zeros, but I want to print just Infeasible, has cycles or loops..
I managed to do it in a hacky way (see below). How to do it more elegantly, without repeating the condition for feasibility? Is there a way to detect infeasibility that does not depend on the problem being solved?
param Vsize := 3;
set V "Vertices" := (0..Vsize-1);
set E "Edges" within V cross V := {(0, 1), (1, 2), (2, 0)};
var v{i in V} >= 0;
minimize rank_total: sum{i in V} v[i];
edge{(i, j) in E}: v[j] - v[i] >= 1;
solve;
printf "#OUTPUT:\n";
printf (if ((exists{i in V} v[i] >= 1) or card(E) = 0) then "" else "Infeasible, has cycles or loops.\n");
printf{i in V} (if ((exists{j in V} v[j] >= 1) or card(E) = 0) then "v_%d: %d\n" else ""), i, v[i];
printf "#OUTPUT END\n";
end;
I tried to declare param Feasible binary := (exists{i in V} v[i] >= 1) or card(E) = 0; but GLPK refused it with Model processing error. When I declared it before solve, it said operand preceding >= has invalid type, when after, it said expression following := has invalid type. I was seeking something like a variable in common programming languages.
In AMPL you can check the built-in parameter solve_result to see if the problem is infeasible:
if solve_result = 'infeasible' then
print 'Infeasible, has cycles or loops.';
However, I'm not sure if GLPK supports this parameter in which case you might need to check for feasibility manually.
As for the error, since exists is a logical expression you can't use it as a numeric one. The fix is to simply put logical expression in if:
param Feasible binary :=
if (exists{i in V} v[i].val >= 1) or card(E) = 0 then 1;

Maple Sequence Length

I'm trying to create a basic program in Maple that runs the Collatz sequence when given a number (n) from the user. For those that don't know, the Collatz sequence is basically "If the number given is odd, do 3n + 1, if it is even, divide by 2 and continue to do so for every answer. Eventually, the answer will reach 1"
I'm trying to grab the number of iterations that the sequence is performed, say if the sequence is run through 10 times, it prints that out. Here is my current code:
Collatz := proc (n::posint)
if type(n, even) then (1/2)*n
else 3*n+1
end if
end proc
CollSeq := proc (n::posint)
local i;
i := n;
while 1 < i do
lprint(i);
i := Collatz(i)
end do
end proc
This so far works, and if the proc CollSeq(50) is entered, it will perform the Collatz sequence on 50 until it reaches 1. The bit I am stuck on is the length of the sequence. I have read around and learned that I might be able to use the nops([]) function of Maple to get the length of the sequence. Here is what I have tried:
CollLen := proc (n::posint)
local c;
c := CollSeq(n);
print(nops([c]))
end proc
I have a feeling this is horribly wrong. Any help would be much appreciated.
Many Thanks
Your function fails to return the actual sequence of values. You need to accumulate it as you go through the loop.
CollSeq := proc (n::posint)
local i, s;
i := n;
s := i;
while 1 < i do
lprint(i);
i := Collatz(i);
s := s, i;
end do;
s;
end proc
The lprint() command just prints its argument to the terminal (showing it on screen), it DOES not save it in a list. And nops() or a better command numelems() counts the number of elements in a list! So putting nops around something that has lprint will not count the number of things. Instead of using lprint in your second function (procedure), define a list, or better than list, an array and in the lprint-line, use a command to append the new number to your growing collection. If you want to see these numbers, just print this collection. Now this time, your third function can have a meaning and it will work as you expected.
Here is the closest fix to your codes.
Collatz := proc( n :: posint )
if type(n, even) then
return( n/2 ):
else
return( 3*n+1 ):
end if:
end proc:
CollSeq := proc ( n :: posint )
local
i :: posint,
c :: 'Array'( posint ):
i := n:
c := Array([]):
while 1 < i do
ArrayTools:-Append( c, i ):
i := Collatz( i ):
end do:
return( c ):
end proc:
CollLen := proc ( n :: posint )
local c :: posint:
c := CollSeq( n ):
return( numelems( c ) ):
end proc:
Here is a screenshot of using them in a Maple worksheet.
Why do I use an array and not a list? Because if you use a list which is immutable, each time you want to add an element to it, in fact it is defining a new list. It is not a memory efficient way, while array is mutable and your edits modifies the array itself. See the help pages on these in Maple.
And looking at your codes, it seems you have the same problem that some of my students in their first programming course usually have, return and print are not the same thing. If you really want a "print" action, that is fine, but you should not expect that the printed value be the output of the function unless you are using a return line inside the function that returns the same value of the print as well. For example you can have print(c): before return(c): in the second function above. So it both prints the sequence on the terminal and returns it to be used by another function or line of code.