macro rule only matches first pattern - macros

I wrote a macro for sym-linking files. At first I had just the first pattern, but then thought that it would be nice not to have to write "&format!" all the time.
So with these patterns:
macro_rules! syml {
($a:expr, $b:expr) => {
Command::new("ln").args(&["-s", $a, $b])
};
( ($a:expr, $($x:expr),+), ($b:expr, $($y:expr),+) ) => {
Command::new("ln").args(&["-s", &format!($a, $($x),+), &format!($b, $($y),+)])
}
}
I want to match these cases:
syml!("from", "to");
syml!(("{}", from), "to");
syml!("from", ("{}", to));
syml!(("{}", from), ("{}", to));
syml!(("{}{}", from, here), ("{}{}", to, there));
But so far every time only the first pattern is matched, so I'm getting mismatched types errors like expected reference, found tuple.
I don't understand why, even for the last two example cases, it tries to match the first pattern and not the second.

As #MatthieuM points out in a comment, a tuple is an expression and macro rules are tried in order.
So in your case:
macro_rules! syml {
($a:expr, $b:expr) => {
Command::new("ln").args(&["-s", $a, $b])
};
( ($a:expr, $($x:expr),+), ($b:expr, $($y:expr),+) ) => {
Command::new("ln").args(&["-s", &format!($a, $($x),+), &format!($b, $($y),+)])
}
}
The first rule will always match any time the second would. The solution is to swap them around:
macro_rules! syml {
( ($a:expr, $($x:expr),+), ($b:expr, $($y:expr),+) ) => {
Command::new("ln").args(&["-s", &format!($a, $($x),+), &format!($b, $($y),+)])
};
($a:expr, $b:expr) => {
Command::new("ln").args(&["-s", $a, $b])
}
}
(Playground)
The above doesn't cover two of your test cases where you mix tuples and strings:
syml!(("{}", from), "to");
syml!("from", ("{}", to));
This can be simply fixed by adding new cases (in order). (I don't know if it's possible to factor out the tuple/string matching, but would be interested in seeing any solutions.)
macro_rules! syml {
( ($a:expr, $($x:expr),+), ($b:expr, $($y:expr),+) ) => {
Command::new("ln").args(&["-s", &format!($a, $($x),+), &format!($b, $($y),+)])
};
( $a:expr, ($b:expr, $($y:expr),+) ) => {
Command::new("ln").args(&["-s", $a, &format!($b, $($y),+)])
};
( ($a:expr, $($x:expr),+), $b:expr ) => {
Command::new("ln").args(&["-s", &format!($a, $($x),+), $b])
};
($a:expr, $b:expr) => {
Command::new("ln").args(&["-s", $a, $b])
}
}
(Playground)

Related

How to create custom operator from a pipe of operators in IxJS?

In rxjs6, we can create an operator from a pipe of operators.
import { pipe } from 'rxjs';
function doSomething() {
return pipe(
map(...),
flatMap(...),
);
}
$.pipe(
map(...),
doSomething(),
flatMap(...),
)
Is there a way to create an operator like this in IxJS?
You can combine operators manually:
import { IterableX as Iterable } from 'ix/iterable';
import { map, filter } from 'ix/iterable/pipe/index';
function customOperator() {
return source$ => map(x => x * x)(
filter(x => x % 2 === 0)
(source$)
);
}
const results = Iterable.of(1, 2, 3, 4).pipe(
customOperator()
).forEach(x => console.log(`Next ${x}`));
Or write your own pipe implementation:
const pipe = (...fns) =>
source$ => fns.reduce(
(acc, fn) => fn(acc),
source$
);
function customOperator() {
return pipe(
filter(x => x % 2 === 0),
map(x => x * x)
)
}

How to write a macro in Rust to match any element in a set?

In C, I'm used to having:
if (ELEM(value, a, b, c)) { ... }
which is a macro with a variable number of arguments to avoid typing out
if (value == a || value == b || value == c) { ... }
A C example can be seen in Varargs `ELEM` macro for use with C.
Is this possible in Rust? I assume it would use match. If so, how would variadic arguments be used to achieve this?
macro_rules! cmp {
// Hack for Rust v1.11 and prior.
(#as_expr $e:expr) => { $e };
($lhs:expr, $cmp:tt any $($rhss:expr),*) => {
// We do this to bind `$lhs` to a name so we don't evaluate it multiple
// times. Use a leading underscore to avoid an unused variable warning
// in the degenerate case of no `rhs`s.
match $lhs { _lhs => {
false || $(
cmp!(#as_expr _lhs $cmp $rhss)
) || *
// ^- this is used as a *separator* between terms
}}
};
// Same, but for "all".
($lhs:expr, $cmp:tt all $($rhss:expr),*) => {
match $lhs { _lhs => {
true && $( cmp!(#as_expr _lhs $cmp $rhss) ) && *
}}
};
}
fn main() {
let value = 2;
if cmp!(value, == any 1, 2, 3) {
println!("true! value: {:?}", value);
}
if cmp!(value*2, != all 5, 7, 1<<7 - 1) {
println!("true! value: {:?}", value);
}
}
First off, if your a, b, and c are concrete values, you can just use match:
fn main() {
let x = 42;
match x {
1 | 2 | 3 => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
}
If you want to match on variables you need to write the match arms like this:
match x {
x if x == a || x == b || x == c => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
…which is basically what you want to avoid.
But: A pretty direct translation of your C macro is also possible!
macro_rules! elem {
($val:expr, $($var:expr),*) => {
$($val == $var)||*
}
}
fn main() {
let y = 42;
let x = 42;
if elem!(x, 1, 3, y) {
println!("{}", x);
}
}
I'm partial to writing this without a macro, taking advantage of contains on arrays.
fn main() {
if [1, 2, 3, 4].contains(&4) {
println!("OK");
}
}
It's hard to predict what will happen to this when optimized, but if absolute performance is a goal you'd do well to benchmark each approach.
Yes this is possible, the following macro expands to do each check.
macro_rules! elem {
($n:expr, $( $hs:expr ),*) => ($( $n == $hs )||* );
}
fn main() {
if elem!(4, 1, 2, 3, 4) {
println!("OK");
}
}
Thanks to #vfs on #rust in IRC.

How to use variadic macros to call nested constructors?

I'm trying to create a macro in Rust that lets me write
make_list!(1, 2, 3)
instead of
Node::new(1, Node::new(2, Node::new(3, None)))
which should work for an arbitrary number of "parameters" including zero. This is what I have so far:
macro_rules! make_list(
() => (
None
);
( $x:expr, $( $more:expr ),* ) => (
Node::new($x, make_list!( $( $more ),* ))
)
);
but I get the following error:
error: unexpected end of macro invocation
--> src/main.rs:19:42
|
19 | Node::new($x, make_list!( $( $more ),* ))
| ^^^^^
I can't make much sense of this. From what I can tell, it should work. What did I do wrong?
The complete code:
type List<T> = Option<Box<Node<T>>>;
struct Node<T> {
value: T,
tail: List<T>,
}
impl<T> Node<T> {
fn new(val: T, tai: List<T>) -> List<T> {
Some(Box::new(Node::<T> {
value: val,
tail: tai,
}))
}
}
macro_rules! make_list(
() => (
None
);
( $x:expr, $( $more:expr ),* ) => (
Node::new($x, make_list!( $( $more ),* ))
)
);
fn main() {
let _list: List<i32> = make_list!(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
Expanding on the error: you get down to the case where there is only one value, and so it writes make_list!(1). However, there is no rule that will match that, for the second rule, after consuming the expression x, wants a comma, which is not provided.
So you need to make it so that it will work for make_list!(1) and not just (in fact, just not) make_list!(1,). To achieve this, get the comma inside the repeating part, like this:
macro_rules! make_list(
() => (
None
);
( $x:expr $( , $more:expr )* ) => (
Node::new($x, make_list!( $( $more ),* ))
)
);
Bonus: you can write make_list![1, 2, 3] instead of make_list!(1, 2, 3) if you want.
As noted by #chris-morgan's answer, expanding the single argument case isn't accounted for.
So you can either include comma in the expansion, or add a single case in the macro:
Example of both, single argument:
macro_rules! make_list {
() => (
None
);
($x:expr) => (
Node::new($x, None)
);
($x:expr, $($more:expr),+) => (
Node::new($x, make_list!($($more),*))
);
}
Including the comma in the expansion:
macro_rules! make_list {
() => (
None
);
($x:expr $(, $more:expr)*) => (
Node::new($x, make_list!($($more),*))
);
}
Here is a fully working example based on the question and updated for Rust 1.14:
type List<T> = Option<Box<Node<T>>>;
#[derive(Debug)]
struct Node<T> {
value: T,
tail: List<T>
}
impl<T> Node<T> {
fn new(val: T, tai: List<T>) -> List<T> {
Some(Box::new(Node::<T> { value: val, tail: tai }))
}
}
macro_rules! make_list {
() => (
None
);
($x:expr $(, $more:expr)*) => (
Node::new($x, make_list!($($more),*))
);
}
fn main() {
let list: List<i64> = make_list!();
println!("{:?}", list);
let list: List<i64> = make_list!(1);
println!("{:?}", list);
let list: List<i64> = make_list!(1,2,3,4,5,6,7,8,9);
println!("{:?}", list);
}

Why are some hashes initialized using curly braces, and some with parentheses?

I'm looking at the following code demonstrating nested hashes:
my %HoH = (
flintstones => {
husband => "fred",
pal => "barney",
},
jetsons => {
husband => "george",
wife => "jane",
"his boy" => "elroy", # Key quotes needed.
},
simpsons => {
husband => "homer",
wife => "marge",
kid => "bart",
},
);
Why is it that the upper-most hash (starting line 1) is initialized using parentheses, whereas the sub-hashes are initialized using curly braces?
Coming from a python background I must say Perl is quite odd :).
Coming from a Perl background I find Perl quite odd, too.
Use parentheses to initialize a hash (or an array). A hash is a map between a set of strings and a set of scalar values.
%foo = ( "key1", "value1", "key2", "value2", ... ); # % means hash
%foo = ( key1 => "value1", key2 => "value2", ... ); # same thing
Braces are used to define a hash reference. All references are scalar values.
$foo = { key1 => "value1", key2 => "value2", ... }; # $ means scalar
Hashes are not scalar values. Since the values in a hash must be scalars, it is therefore not possible to use a hash as a value of another hash.
%bar = ( key3 => %foo ); # doesn't mean what you think it means
But we can use hash references as values of another hash, because hash references are scalars.
$foo = { key1 => "value1", key2 => "value2" };
%bar = ( key3 => $foo );
%baz = ( key4 => { key5 => "value5", key6 => "value6" } );
And that is why you see parentheses surrounding a list of lists with braces.
The essential difference (....) is used to create a hash. {....} is used to create a hash reference
my %hash = ( a => 1 , b => 2 ) ;
my $hash_ref = { a => 1 , b => 2 } ;
In a bit more detail - {....} makes an anonymous hash and returns a reference to it wich is asigned to the scalar $hash_ref
edited to give a bit more detail
First, the parens do nothing but change precedence here. They never have nothing to do with list creation, hash creation or hash initialisation.
For example, the following two lines are 100% equivalent:
{ a => 1, b => 2 }
{ ( a => 1, b => 2 ) }
For example, the following two lines are 100% equivalent:
sub f { return ( a => 1, b => 2 ) } my %hash = f();
sub f { return a => 1, b => 2 } my %hash = f();
Second, one doesn't initialise a hash using { }; one creates a hash using it. { } is equivalent to my %hash;, except that the hash is anonymous. In other words,
{ LIST }
is basically the same as
do { my %anon = LIST; \%anon }
(but doesn't create a lexical scope).
Anonymous hashes allows one to write
my %HoH = (
flintstones => {
husband => "fred",
pal => "barney",
},
jetsons => {
husband => "george",
wife => "jane",
"his boy" => "elroy",
},
simpsons => {
husband => "homer",
wife => "marge",
kid => "bart",
},
);
instead of
my %flintstones = (
husband => "fred",
pal => "barney",
);
my %jetsons = (
husband => "george",
wife => "jane",
"his boy" => "elroy",
);
my %simpsons = (
husband => "homer",
wife => "marge",
kid => "bart",
);
my %HoH = (
flintstones => \%flinstones,
jetsons => \%jetsons,
simpsons => \%simpsons,
);

How do I sort by value from a second level hash, in Perl?

my $hash_ref = {
one => { val => 1, name => 'one' },
three => { val => 3, name => 'three'},
two => { val => 2, name => 'two' },
};
I would like to sort $hash_ref such that a foreach would order them by
$hash_ref->{$key}->{'val'}
one
two
three
Any suggestions?
#sorted_list is an array of references to the sorted hash elements:
#sorted_list = sort { $a->{'val'} <=> $b->{'val'} } values %{$unsorted_hash_ref};
You can use it like so:
#!/usr/bin/perl
my $hash_ref = {
one => { val => 1, name => 'one' },
three => { val => 3, name => 'three' },
two => { val => 2, name => 'two' },
};
foreach $elem ( sort { $a->{'val'} <=> $b->{'val'} } values %{$hash_ref} ) {
print "$elem->{'val'} : $elem->{'name'}\n";
}
Output:
1 : one
2 : two
3 : three
Hash tables don't have any specific order. However, you can sort the keys in an array and use that to iterate through the hash:
my $hash_ref = {
one => { val => 1, name => 'one'},
three => { val => 3, name => 'three'},
two => { val => 2, name => 'two'},
};
use strict;
use warnings;
use Lingua::EN::Words2Nums;
foreach my $key (sort { words2nums($a) <=> words2nums($b) } keys %$hash_ref)
{
# do something with $hash_ref->{$key}
print "processing key $key.\n";
}
You can define anything you like as a sort method; see perldoc -f sort for more details. Conversion from ordinal numerical text to arithmetic values is done with Lingua::EN::Words2Nums (it does cardinal numbers too).
use strict;
use warnings;
my %hash_ref = (
one => { val => 1, name => 'one' },
three => { val => 3, name => 'three'},
two => { val => 2, name => 'two' },
);
foreach my $key(sort {$hash_ref{$a}{val} <=> $hash_ref{$b}{val}} keys %hash_ref) {
my $value = $hash_ref{$key}{val};
my $name = $hash_ref{$key}{name};
print "$name -> $value\n";
}
output:
one -> 1
two -> 2
three -> 3
#!/usr/bin/perl
my $hash_ref = (
one => {val => 1, name => "one"},
three => {val => 3, name => "three"},
two => {val => 2, name => 'two'},
);
foreach $elem( sort {$$hash_ref{$a}{val} <=> $$hash_ref{$b}{val}} keys %$hash_ref){
my $value = $hash_ref->{$elem}{val};
my $name = $hash_ref->{$elem}{name};
print "$name -> $value\n";
}
OutPut:
one -> 1
two -> 2
three -> 3