A 'compiler' for list comprehension in Rebol - list-comprehension

I want to compile this list comprehension:
>> lc [reduce [x y] | x in [1 2 3] y in [4 5 6]]
== [[1 4] [1 5] [1 6] [2 4] [2 5] [2 6] [3 4] [3 5] [3 6]]
in:
collect [
foreach x [1 2 3] [
foreach y [4 5 6] [
keep/only reduce [x y]]]]
or:
>> lc [reduce [x y] | x in range [1 5] y in range reduce[1 x] if x + y > 4]
== [[3 2] [3 3] [4 1] [4 2] [4 3] [4 4] [5 1] [5 2] [5 3] [5 4] [5 5]...
in:
collect [
foreach x range [1 5] [
foreach y range reduce [1 x] [
if x + y > 4 [keep/only reduce [x y]]]]]
or:
>> lc/flat [reduce [x y] | x in range [1 5] y in range reduce [1 x] if x + y > 4]
== [3 2 3 3 4 1 4 2 4 3 4 4 5 1 5 2 5 3 5 4 5 5]
in:
collect [
foreach x range [1 5] [
foreach y range reduce [1 x] [
if x + y > 4 [keep reduce [x y]]]]]
My ugly implementation in Red is:
fx: func [code] [func [x] code]
lc: function [list-comp /flat] [ ; list-comp = [code | generators [opt if test]]
flat: any [flat false]
var: none
part-gen: part-if: rest: code: []
rule-var: [set var word! 'in]
list: copy []
generator+if: [copy part-gen to 'if copy part-if to end]
generator: [copy part-gen to end]
emit: fx [append/only list x]
parse list-comp [
copy code to '| skip [
generator+if
|
generator ]
]
parse part-gen [
some [
rule-var (emit var) copy rest to [rule-var | end ] (emit rest)
]
]
option: either flat [copy [keep]] [copy [keep/only]]
code: append option code
if part-if <> [] [code: append/only part-if code]
foreach [l v] reverse list [code: compose [foreach (v) (l) (reduce [code])]]
collect code
]
; from hof.r
range: func [
{Makes a block containing a range of ord! values.
Format: .. [1 5] == [1 2 3 4 5]
.. [1 3 6] == [1 2 5]
.. [2 2 6] == [2 2 2 2 2 2]
}
xs [block!] {either [start end] or [start next end]}
/local range x1 x2 delta result [block!]
][
range: reduce xs
x1: range/1
either range/3 [
x2: range/3
delta: (range/2 - x1)
][
x2: range/2
delta: 1
]
;result: make block! (x2 - x1) / delta
result: copy []
either delta <> 0 [
result: reduce [x1]
loop x2 - x1 [
append result delta + last result
]
][
loop absolute x2 [
insert tail result x1
]
]
result
]
The program does not flow, it is full of variables, of if and append (I had difficulty with parse; compose, in this case, is limited).
Is there a more "rebolish" way (in rebol2/rebol3/red/ren-c) to resolve this problem?
Update: My implementation of the 'compiler' is only an indication of what I intend to do. I am not asking for a correction of my program (I could not even report it) but for a solution that better uses parse and builds the code more clearly.

List-comprehension is a great exercise in dialect building: it's moderately small, has a well-defined purpose and, in general, serves as a code kata for aspiring young Grasshopper โ€” there is no single "right" way to do it, but many better or worse solutions.
"Rebolish" way would be to stay pragmatic, start with use-cases, and let the problem domain guide you โ€” perhaps you're solving Euler project and need a set-theoretic library, perhaps what you want are LINQ-like database queries, maybe it's just for the sake of learning and the pleasure of reinventing the wheel, who knows?
While thinking about it, you might realize that you actually don't need list comprehensions, and that's good! The leanest code is the one never being written, and the cleverest solution is the one that nips the problem in the bud by redefining it in simpler terms.
Assuming you're just dabbling with metaprogramming or learning Red without any specific problem in mind, and taking my initial comment with your subsequent edit into account, here are my 2ยข:
Start with a grammar
List comprehension, as a syntactic construct, has a well-defined form. Consulting respective wiki page makes it possible to define a basic grammar right away:
set-builder: [expression '| some generator predicate]
expression: [to '|]
generator: [word! 'in to [generator | predicate | end]]
predicate: ['if to end]
Figure out what to emit
You got that covered already: for each generator in set-builder notation we need an extra layer of foreach; the body of inner foreach should have the form <predicate> [<keep> <expression>].
Define an interface
Instead of /flat I'd use /only, as this is a well-known idiom. Since there are a lot of set-word!s in our function's body, I'll use function constructor:
list: function [spec [block!] /only][...]
Connect the dots
Proceed in baby steps and start simple:
Parse the spec with set-builder and extract the relevant parts for further processing.
Compose extracted parts together.
Step 1
We need to modify our grammar accordingly: add collect / keep where needed and counter edge-cases.
There are 3 parts that we need to extract: expression, generators, and predicate. We can group generators together by adding an extra collect:
set-builder: [collect [expression '| collect some generator predicate]]
expression is straightforward:
expression: [keep to '|]
So as predicate, but we need to keep the if also:
predicate: [ahead 'if keep to end]
But generator is trickier, for two reasons:
There's such a thing called partial match. We cannot just write:
generator: [keep word! 'in keep to [generator | predicate | end]]
When generator or predicate matches inside to, it will recursively keep extra data because of the word! or to end match, messing up the extracted block.
keep behaves differently depending on the number of values being kept: it keeps single value as-is, but groups many of them together.
[1 2 3] -> foreach x [1 2 3] ..., not foreach x 1 2 3 ...
[range [4 5 6]] -> foreach y range [4 5 6] ...
So, what we need is (a) a rule that will check that the thing we look at is indeed a generator, without extracting anything (word! 'in should do) and (b) a slight modification to keep that will always extract a block! โ€” keep copy dummy-word. Lo and behold:
generator: [keep word! 'in keep copy range to [word! 'in | 'if | end]]
Now mash all of that together:
set-builder: [collect [expression '| collect some generator predicate]]
expression: [keep to '|]
generator: [keep word! 'in keep copy range to [word! 'in | 'if | end]]
predicate: [ahead 'if keep to end]
set [expression ranges: clause:] parse spec set-builder
Note that I use set-word!s inside a block to subvert function to our cause. ranges contains, well, ranges, each in turn containing a word to iterate with and a range itself. clause is either a block! (if it was present in spec) or none! (if there wasn't any).
Step 2
First thing first, we compose the body block of inner foreach:
body: [<clause> [<keep> <expression>]]
This leads to:
body: compose/deep [(any [clause 'do]) [(pick [keep/only keep] only) (expression)]]
Which covers two extra cases: the absence of predicate (unconditional evaluation) and the presence of /only refinement.
Let's figure out how each layer of subsequent foreach looks like:
layer: [foreach <word> <range> <body>]
<word> can be used as-is; <range> might be spliced; <body> is either a body or an innermost layer. Because of the range splicing (i.e. peeling off an extra layer of [...] from extracted data), we cannot use compose/only, so we need wrap <body> in a block and use compose/deep:
layer: [foreach (word) (range) [(body)]]
One last thing: we extracted data from top to bottom, but we need to accrue it the other way around, by layering foreach one on another, starting with body. So we need to reverse the block of ranges:
foreach [range word] reverse ranges [...]
All set! Now just slap collect on top and track body to wrap over on next iteration:
collect foreach [range word] reverse ranges [body: compose/deep layer]
And the whole thing is:
list: function [
"List comprehension"
spec [block!]
/only
][
#1
set-builder: [collect [expression '| collect some generator predicate]]
expression: [keep to '|]
generator: [keep word! 'in keep copy range to [word! 'in | 'if | end]]
predicate: [ahead 'if keep to end]
set [expression ranges: clause:] parse spec set-builder
#2
body: compose/deep [(any [clause 'do]) [(pick [keep/only keep] only) (expression)]]
layer: [foreach (word) (range) [(body)]]
collect foreach [range word] reverse ranges [body: compose/deep layer]
]
Examples:
>> list [as-pair x y | x in [1 2 3] y in [4 5 6]]
== [1x4 1x5 1x6 2x4 2x5 2x6 3x4 3x5 3x6]
>> list/only [reduce [x y] | x in range [1 5] y in range reduce [1 x] if x + y > 4]
== [[3 2] [3 3] [4 1] [4 2] [4 3] [4 4] [5 1] [5 2] [5 3] [5 4] [5 5]]
Is it good? If it helped you to grok Red, Parse and dialects just a little bit, then I think it might be. Is it bad? If so, then you can learn from my mistakes and do better.
In any case, if you find yourself struggling with Parse, there is a reference documentation in the pipeline that you might want to skim through and parse Gitter room where you can ask for help.
Once you refactored your code and is happy with it, share the joy in Red community chat and get some feedback. Until then, take care!

Related

How can i use conditionals in list comprehension?

I am trying to build a list of 0's using list comprehension. But i also want to make an index 1 where i choose in the list. For example myList 5 2 = [0,1,0,0,0] where 5 is the number of elements and 2 is the index.
myList el index = [0 | n <- [1..el], if n == index then 1 else 0]
but this results in an error.
The smallest change that fixes that is
myList el index = [if n == index then 1 else 0 | n <- [1..el]]
Note that what's at the left of | is what generates the list elements. A list comprehension of the form [ 0 | ...] will only generate zeros, and the ... part only decides how long is the resulting list.
Further, in your code the compiler complains because at the right of | we allow only generators (e.g. n <- someList), conditions (e.g. x > 23), or new definitions (let y = ...). In your code the if ... is interpreted to be a condition, and for that it should evaluate to a boolean, but then 1 makes the result a number, triggering a type error.
Another solution could be
myList el index = replicate (index-1) 0 ++ [1] ++ replicate (el-index) 0
where replicate m 0 generates a list with m zeros, and ++ concatenates.
Finally, note that your index is 1-based. In many programming languages, that's unconventional, since 0-based indexing is more frequently used.

MATLAB:how can I use fprintf or sprintf to print matrix and vectors with strings togheter?

I'm at the beginning with the MATLAB software ,and I have two questions:
1) If I want to print a matrix, preceeded by a string, using the fprintf command, how can I do?
For example, to print a matrix alone, I use
fprintf([repmat('%d\t', 1, size(r, 2)) '\n'], r');
But how can I print a string followed by the matrix, all togheter in fprintf, without usind the disp function ?
For example, if I want to print:
>>The matrix you inserted is [1 3; 4 6]
2) How can I do the same thing with vectors (I know it's only a particular case of matrix) ?
I actually use, for example:
>>vectorname=[1 5 2];
>>strtrim(sprintf('%d ', vectorname));
And it's ok for the only numbers of the vector, but If I insert a string in spintf the result is:
>>vectorname=[1 5 2];
>>strtrim(sprintf('Your vector is: %d ', vectorname))
>>Your vector is 1 Your vector is 5 Your vector is 2
How can I inglobe the numbers, one ofter the other, with only one command (sprintf, fprintf, ecc.)??
Thank you very much for the help!
In both cases you can use mat2str.
First case:
input_mat = [1 3; 4 6];
sprintf(['The matrix you inserted is ' mat2str(input_mat)])
ans =
The matrix you inserted is [1 3;4 6]
Second case:
vectorname=[1 5 2];
sprintf(['Your vector is: ', mat2str(vectorname)])
ans =
Your vector is: [1 5 2]

Explaining the behavior of sprintf in MATLAB

I have recently been perplexed by some of the behavior of sprintf. It appears to be inconsistent in my view and I haven't been able to exactly determine the logic behind some of its behavior. Here is one case that baffles me
C = [1 + 2i, 3+ 4i, 5+6i, 7+8i, 9+10i];
>> sprintf('%+d%+di\n',real(C(1:2)),imag(C(1:2)))
ans =
+1+3i
+2+4i
>> sprintf('%+d%+di\n',real(C(1:3)),imag(C(1:3)))
ans =
+1+3i
+5+2i
+4+6i
I would've expected it to print my complex numbers as in C. Do I need to explicitly create a new vector of interleaved inputs? I feel this is problematic when different types are involved.
EDIT: What I feel the expected output should be
>> sprintf('%+d%+di\n',real(C(1:2)),imag(C(1:2)))
ans =
+1+2i
+3+4i
>> sprintf('%+d%+di\n',real(C(1:3)),imag(C(1:3)))
ans =
+1+2i
+3+4i
+5+6i
The sprintf command you have provided behaves as sprintf('%+d%+di\n', [1, 3], [2, 4]) since real(C(1:2)) returns [1, 3] and imag(C(1:2)) returns [2, 4] which behaves as you've observed.
What you want to do is: sprintf('%+d%+di\n', [1, 3; 2, 4])
It should be accomplished by either looping over elements of C or with the following sprinf('%+d%+di\n', [real(C(1:2)); imag(C(1:2))]).

Combining a matrix of numbers, with array of strings

I am trying to compute the code seen below, in a more clever way, using MatLAB:
f=zeros(5,2) %Values irrelevant.
y = {'A1', 'B1';
f(1,1) f(1,2);
f(2,1) f(2,2);
f(3,1) f(3,2);
f(4,1) f(4,2);
f(5,1) f(5,2)};
What I get out is the f matrix with the text of A1 and B1 above the two vectors.
I suppose there is a simpler way to write this, for more complex purposing, but I've tried any combination of parenthesis, brackets and curly brackets, num2str that I could think of.
Any suggestions?
The simplest solution is to use num2cell and concatenate the result with the strings:
y = [{'A1', 'B1'}; num2cell(f)];
Example
>> f = reshape(1:10, 2, [])';
>> y = [{'A1', 'B1'}; num2cell(f)]
y =
'A1' 'B1'
[ 1] [ 2]
[ 3] [ 4]
[ 5] [ 6]
[ 7] [ 8]
[ 9] [10]

How to get all elements from a list without the last element?

The following Groovy code
lines = ['0','1','2','3','4','5']
println lines[1..lines.size()-1]
println lines[1..-1]
println lines[1..<lines.size()-1]
println lines[1..<-1]
println lines[1..<-2]
println lines[1..-2]
produces this output:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 0]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
Since -1 is the index of the last element in the list, the first two make sense (ranges in Groovy include the end element instead of omitting it as everywhere else in Java :-( )
Line #3 is the desired output (list without first and last element).
I'm worried about the output #4: Why do I get [1, 0] for 1..-1?
Also [1, 2, 3, 4, 5] for the range 1..<-2 seems wrong.
Why does that happen?
The best way to take all elements but the last one, in my opinion, is to use the take method:
def list = ['a', 'b', 'c']
assert list.take(list.size() - 1) == ['a', 'b']
It behaves properly in the corner case where size == 1:
def list = ['one']
assert list.take(list.size() - 1) == []
Though I'd prefer it to throw an exception in the case size == 0, but the behavior is not that bad:
def list = []
assert list.take(list.size() - 1) == []
You can also use list[0..<list.size()-1] (your third example) and it will behave the same except for the empty list, in which case it will throw an ArrayIndexOutOfBoundsException, but i think is not as readable as the take counterpart.
Another acceptable solution is using list[0..-2] (your last example), which i think looks much more elegant, but unfortunately breaks when size == 1 with an ArrayIndexOutOfBoundsException.
In your examples (i'll assume that you meant to use 0 as the starting index instead of 1 if you wanted to include all elements but the last one):
lines[0..lines.size()-1] is equivalent to lines[0..-1] because the getAt(Range) method of lists will treat ranges with negative indexes the same way asgetAt(Integer) does, i.e. as accessing the (list.size() + negativeIndex)'th element of the list. Therefore list[0..-1] is the same as saying "from first element to last" and it's the same as copying the list; and list[-1..0] is the same as "from last to first" and it's equivalent to list.reverse() :)
The problem with the other non-inclusive range example is that the non-inclusive ranges are being evaluated before the list access and they evaluate to an incorrect inclusive range. For example, 0..<-2 evaluates to 0..-1, and that's why it's returning all the elements. 0..<-1 evaluates to 0..0 and it returns only the first element.
Notice that the empty range is a special case. It's denoted as 0..<0 and it doesn't have an inclusive equivalent (so Groovy won't do any magic conversion here). And that's why list[0..<list.size()-1] works when size == 1 (the range evaluates to the empty range) while list[0..-2] doesn't :)
Maybe this has changed since epideman wrote his answer, but you can get the whole list without the last element with 0..<-1:
assert ["foo"][0..<-1] == []
assert ["foo", "bar"][0..<-1] == ["foo"]
assert ["foo", "bar", "baz"][0..<-1] == ["foo", "bar"]
// blows up if empty, here take is better
assert [][0..<-1] == [] // BOOM
// if you want null safe, use take
assert [].take(-1) == []
This is with groovy 2.2.1.
Since Groovy 2.4 you can use the init() method:
lines = ['0','1','2','3','4','5']
assert lines.init() == ['0','1','2','3','4']

Resources