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.

Tags:

Raku