Why does each behave differently inside word definitions? - each

To do a quotation for each in an array:
(scratchpad) { "3.1415" "4" } [ string>number ] each
3.1415
4
To do this inside a word:
(scratchpad) : conveach ( x -- y z ) [ string>number ] each ;
(scratchpad) { "3.1415" "4" } conveach .
But this throws an error:
The word conveach cannot be executed because it failed to compile
The input quotation to “each” doesn't match its expected effect
Input Expected Got
[ string>number ] ( ... x -- ... ) ( x -- x )
What am I doing wrong?

Factor requires all words to have a known stack effect. The compiler wants to know how many items the word eats from the stack and how many it puts back. In the listener, the code you type in doesn't have that restriction.
{ "3.1415" "4" } [ string>number ] each
Takes no items from the stack but puts two there. The stack effect would be denoted as ( -- x y ).
[ string>number ] each
This code on the other hand, takes one item but puts 0 to many items back on the stack. The number varies depending on how long the sequence given to each is.
! ( x -- x y z w )
{ "3" "4" "5" "6" } [ string>number ] each
! ( x -- )
{ } [ string>number ] each
! ( x -- x )
{ "2" } [ string>number ] each
You probably want to use the map word instead which would transform your sequence from one containing strings, to one containing numbers. Like this:
: convseq ( strings -- numbers ) [ string>number ] map ;
IN: scratchpad { "3" "4" } convseq .
{ 3 4 }

Related

Initialise a list element referring to an earlier element from the same list

Given the list
l = [ "ALPHA ONE", 123, _( "Alpha One" ), _( "ALPHA ONE" ) ]
where elements 2 and 3 (the translated text) are both tied directly to element 0.
Is it possible to define the list in which elements 2 and 3 dynamically refer to element 0? Is there a notation/mechanism for a list element to be initialised by referring to an earlier element?
For example, something along the lines of
l = [ "ALPHA ONE", 123, _( [ 0 ].title() ), _( [ 0 ].upper() ) ]
This would make life easier/safer as I don't want to have to define essentially the text over and over.
From Python 3.8 this is possible using assignment expressions (the walrus operator). Assign the first element to a name using the walrus operator and then reuse this variable in the other elements
l = [(x := "ALPHA ONE"), 123, _(x.title()), _(x.upper())]
Is there a reason you need to put it in a list literal? Also, this seems more like a tuple.
t = "ALPHA ONE"
l = (t, 123, _(t.title()), _(t.upper()))

length of keys in a map in Terraform - odd output

Given:
variable "foo" {
type = map
default = {
lorem = "ipsum"
dolor = "sit"
}
}
then, in Terraform console, why does
[for k, v in var.foo : length(k) ]
give:
[
4,
5,
]
That's a really odd result. When I run the exact same thing locally, I get the expected result:
> [for k, v in var.foo : length(k) ]
[
5,
5,
]
Do you have a value set for "foo" in a *.auto.tfvars file somewhere? How about an environment variable called TF_VAR_foo? My best guess is that there is a different variable defined somewhere called "foo" that is overwriting the default value specified in the variable declaration.

Race condition when using withLatestFrom as part of my pipeable operator

I have the following code that suffers from a race condition. Sometimes I can see the results, sometimes I cannot:
const op1 = ()=>{
const filesObs = from(['a','b','c']).pipe(delay(200))
return (obs)=>{
return obs
.pipe(delay(100))
.pipe(withLatestFrom(filesObs))
}
}
from([1,2,3,4,5]).pipe(op1()).subscribe(console.log);
As it is I don't see anything printed. But If I increase the 2nd delay to 300 I see the expected values:
[ 1, 'c' ]
[ 2, 'c' ]
[ 3, 'c' ]
[ 4, 'c' ]
[ 5, 'c' ]
Is there a way to always see the result by using observeOn or subscribeOn somewhere on my code or should I follow another best practice?
First of all this is not an issue specific to withLatestFrom being inside an operator.
The code below that is not used inside an operator also does not print anything (faces the same issue):
const filesObs = from(['a','b','c']).pipe(delay(200));
from([1,2,3,4,5]).pipe(delay(100),withLatestFrom(filesObs)).subscribe(console.log);
According to the provided desired output in the question we need to get the last element of the letters stream and pair it with each value from the numbers stream. But what withLatestFrom() will get is the last emitted element at each point in time. To justify this, consider adding some delay between the emitted elements of the letters stream (1st line).
//adding a delay on each of the emitted letters.
const filesObs = from(['a','b','c']).pipe(concatMap((v)=>of(v).pipe(delay(50))))
from([1,2,3,4,5]).pipe(delay(100),withLatestFrom(filesObs)).subscribe(console.log);
[ 1, 'a' ]
[ 2, 'a' ]
[ 3, 'a' ]
[ 4, 'a' ]
[ 5, 'a' ]
As you can see the above is not the desired output.
Also, I am not sure if it is an rxjs bug, but withLatestFrom() will skip the value if there is nothing emitted yet on the observable arguement. See below how it skips the first number (because at the moment it is emits it, nothing has been emitted yet on filesObs).
const filesObs = from(['a','b','c']).pipe(concatMap((v)=>of(v).pipe(delay(50))))
//Now adding a delay on each of the emitted numbers.
from([1,2,3,4,5]).pipe(concatMap((v)=>of(v).pipe(delay(25)))).pipe(withLatestFrom(filesObs)).subscribe(console.log);
[ 2, 'a' ]
[ 3, 'a' ]
[ 4, 'b' ]
[ 5, 'b' ]
solution
One solution to the problem is to get the last() element of the letters stream and repeat() it. Then map each number with the first() element of filesObs, which will now always be the last one ('c'):
const filesObs = from(['a','b','c']).pipe(delay(200), last(), repeat())
from([1,2,3,4,5]).pipe(delay(100)).pipe(mergeMap(v=>filesObs.pipe(first(),map(v2=>[v2,v])))).subscribe(console.log);
And the same inside an operator:
const op1 = ()=>{
const filesObs = from(['a','b','c']).pipe(delay(200), last(), repeat())
return (obs)=>{
return obs
.pipe(delay(100))
.pipe(mergeMap(v=>filesObs.pipe(first(),map(v2=>[v2,v]))))
}
}
from([1,2,3,4,5]).pipe(op1()).subscribe(console.log);
Both of the above will output the below value, independent to the delay values:
[ 'c', 1 ]
[ 'c', 2 ]
[ 'c', 3 ]
[ 'c', 4 ]
[ 'c', 5 ]

Groovy: Adding elements to a collection

I've the following collection
def a = [
b:[
[
c: "x",
d: ["y","z"]
]
],
b1:[
[
c: "x1",
d: ["y1","z1"]
]
]
]
I want to add a new element "w" to d:["y", "z"] to have this d:["y", "z", "w"]
I've tried a.put(d:"w"), a.add(d:"w") but getting exception
groovy.lang.MissingMethodException: No signature of method: java.util.LinkedHashMap.put() is applicable for argument types: (String) values: [w]
You have take into account all your nesting here. You have a map to
list to map. The main problem is the list now, since your example makes
it not clear, how many list items b could hold. So the solution for
your exact example is:
a.b[0].d << "w"

Groovy: String representing a List to an actual List

Is there a way in Groovy to make a list out of a string? i.e. I have a String "[0,1]" , and I want to transform it to [0,1] (and actual groovy list) in order to manipulate.
The values of the string might be bidimensional ( "[ [1], [2, 3]]" ) but it's for sure it will always be a list.
You can use Eval.me but obviously, take care of evaluating any old strings
def a = Eval.me( '[ 1, 2 ]' )
An alternative could be:
def a = new groovy.json.JsonSlurper().parseText( '[ 1, 2 ]' )
As the two list forms you give in your question are both valid Json :-)
PERFORMANCE
Given the following benchmarking code:
#Grab('com.googlecode.gbench:gbench:0.4.1-groovy-2.1') // v0.4.1 for Groovy 2.1
import groovy.json.JsonSlurper
def r = benchmark( measureCpuTime:false ) {
'eval' {
def a = Eval.me( '[ 1, 2 ]' )
assert a == [ 1, 2 ]
}
'json' {
def a = new JsonSlurper().parseText( '[ 1, 2 ]' )
assert a == [ 1, 2 ]
}
}
r.prettyPrint()
I get the output:
eval 4661121
json 7257
So it's much quicker going the json route ;-)

Resources