How do I check if something is an elem of a qw?
If you do :
<1 2 3 4 5>.raku.say
You'll see that it creates a list of IntStr objects (which are objects that can be either a String or an Integer). When we put these in a set we get a Set of IntStr objects.
The problem is (elem)
doesn't use smart-matching but equivalence and neither the Int 1
or the String "1"
are equivalent to IntStr.new(1, "1")
. They do both smart-match with the IntStr...
You probably want to use first
to find the needle :
defined <1 2 3 4 5>.first( 2, :k )
first( $match, :k )
returns the index of the first matching item and Nil
if there's no match. So we test to see if it's defined and if so our item exists.
Or you can cast your list to String ot Integers using a map
.
2 (elem) <1 2 3 4 5>.map( *.Int )
So first off <…>
is actually the same as:
q:w:v < … >
Or more verbosely:
Q :single :words :val < … >
The q
or :single
is how you specify the semantics of single quotes.
'abc'
q 'abc'
Q :q 'abc'
Q :single 'abc'
The :w
or :words
specifies that you want the string to be split on whitespace.
Q :w ' a b c ' eqv 'a', 'b', 'c'
Q :words ' a b c ' eqv 'a', 'b', 'c'
The :v
or :val
specifies that you want val()
semantics.
Using val
will turn a string representing a number into something that is both a string and a number.
Q :v '123'
Q :val '123'
val( '123' )
IntStr.new( 123, '123' )
Note that you can combine Q
, q
, or qq
with a single character option
qw 'a b' =:= q:w 'a b' =:= q:words 'a b'
qb 'a\n' =:= q:b 'a\n' =:= q:backslash 'a\n'
qs 'a$_' =:= q:s 'a$_' =:= q:scalar 'a$_'
(It is probably a bug that qv''
doesn't work.)
At any rate (elem)
and all Set/Bag/Mix operators deal with specific things, not generalities.
1.0 (elem) 1,2,3; # False
1 (elem) 1,2,3; # True
So if anything is different, those operators treat them as different things.
q:v '1' (elem) 1; # False
q:v '1' (elem) IntStr.new(1,'1'); # True
<1> (elem) 1; # False
<1> (elem) IntStr.new(1,'1'); # True
As we've established <1>
wouldn't return an Int
, it returns an IntStr
.
An IntStr
isn't exactly the same as an Int
so it doesn't match an Int
.
Presumably you want numeric things to only be numeric.
< a b c 1 2 3 >.duckmap: -> Numeric $_ { +$_ }
This goes through the list and anything that is already Numeric will get this function called on it.
The function does only one thing, call prefix:« + »
which calls .Numeric
on the value.
Since an IntStr
is an Int
which does Numeric
, the function will be called on those values.
You may be wondering why <…>
uses val
semantics.
The reason is there is no way to know if you plan on giving it to a function that needs a Str
or something Numeric
.
sub foo ( Str ){}
sub bar ( Int ){}
foo <1>;
bar <1>;
foo 1; # Failure
bar '1'; # Failure
check if something is an element
The only coercion that (elem)
applies is the minimum necessary to enable its right hand side argument to accept a set operation. It does not coerce at the element level and instead does strict comparison of values. So you must apply your own explicit coercion, for example:
'2' (elem) qw<1 2 3> ; # True
'2' (elem) ~«<1 2 3> ; # True
'2' (elem) <1 2 3>».Str ; # True
'2' (elem) <1 2 3> .map: *.Str ; # True
2 (elem) +«<1 2 3> ; # True
2 (elem) <1 2 3>».Int ; # True
2 (elem) <1 2 3> .map: *.Int ; # True
'2' (elem) <a 2> .duckmap: *.Int ; # False
2 (elem) <a 2> .duckmap: *.Int ; # True
'a' (elem) <a 2> .duckmap: *.Int ; # False
'2' (elem) <a 2> .duckmap: *.Str ; # True
2 (elem) <a 2> .duckmap: *.Str ; # False
'a' (elem) <a 2> .duckmap: *.Str ; # True
«
/»
distribute an operation to the leaves of their tree or list argument(s). For example, +«
distributes a prefix +
, which coerces its argument to a number.
duckmap
is somewhat similar to «
/»
; one major difference is that it just continues if an operation doesn't apply. That is to say, a trial bind (function call) is attempted for each element; if the type signature matches, the function is called and its return value(s) replace that element, otherwise the element is just retained in the result as is.