我一直在使用perl一段时间了。 我想知道如何在perl中运行以下操作:
subtract(40)(20)
要获得结果:
20
我想我必须查看Perl的自定义解析技术。 这就是我现在正在看的内容:
和 http://www.perl.com/pub/2012/10/an-overview-of-lexing-and-parsing.html
现在,我不确定该寻找什么或做什么。 关于如何解决这个问题的任何帮助,阅读什么将不胜感激。请清楚。 谢谢。
答案 0 :(得分:3)
我建议您尝试Parse::Keyword。 Parse :: Keyword非常适合解析自定义语法,因为它允许您回调Perl解析器的各个部分,例如parse_listexpr
,parse_block
,parse_fullstmt
等(请参阅{{ 3}})。
它有一个缺点,如果你使用它们来解析关闭变量的表达式,这些处理得很糟糕,但这可以用perlapi解决。
Parse :: Keyword(包括PadWalker技巧)是PadWalker使用的;这确实是一些非常复杂的东西!早期版本的p5-mop-redux也使用它。
无论如何,这里有一个演示如何解析你奇怪的函数......
use v5.14;
use strict;
use warnings;
# This is the package where we define the functions...
BEGIN {
package Math::Weird;
# Set up parsing for the functions
use Parse::Keyword {
add => \&_parser,
subtract => \&_parser,
multiply => \&_parser,
divide => \&_parser,
};
# This package is an exporter of course
use parent 'Exporter::Tiny';
our @EXPORT = qw( add subtract multiply divide );
# We'll need these things from PadWalker
use PadWalker qw( closed_over set_closed_over peek_my );
sub add {
my @numbers = _grab_args(@_);
my $sum = 0;
$sum += $_ for @numbers;
return $sum;
}
sub subtract {
my @numbers = _grab_args(@_);
my $diff = shift @numbers;
$diff -= $_ for @numbers;
return $diff;
}
sub multiply {
my @numbers = _grab_args(@_);
my $product = 1;
$product *= $_ for @numbers;
return $product;
}
sub divide {
my @numbers = _grab_args(@_);
my $quotient = shift @numbers;
$quotient /= $_ for @numbers;
return $quotient;
}
sub _parser {
lex_read_space;
my @args;
while (lex_peek eq '(')
{
# read "("
lex_read(1);
lex_read_space;
# read a term within the parentheses
push @args, parse_termexpr;
lex_read_space;
# read ")"
lex_peek eq ')' or die;
lex_read(1);
lex_read_space;
}
return sub { @args };
}
# In an ideal world _grab_args would be implemented like
# this:
#
# sub _grab_args { map scalar(&$_), @_ }
#
# But because of issues with Parse::Keyword, we need
# something slightly more complex...
#
sub _grab_args {
my $caller_vars = peek_my(2);
map {
my $code = $_;
my $closed_over = closed_over($code);
$closed_over->{$_} = $caller_vars->{$_} for keys %$closed_over;
set_closed_over($code, $closed_over);
scalar $code->();
} @_;
}
# We've defined a package inline. Mark it as loaded, so
# that we can `use` it below.
$INC{'Math/Weird.pm'} = __FILE__;
};
use Math::Weird qw( add subtract multiply );
say add(2)(3); # says 5
say subtract(40)(20); # says 20
say multiply( add(2)(3) )( subtract(40)(20) ); # says 100
答案 1 :(得分:2)
如果您可以使用添加的符号和箭头,则可以{/ 3}} subtract
使用
my $subtract = sub {
my($x) = @_;
sub { my($y) = @_; $x - $y };
};
将其称为
my $result = $subtract->(40)(20);
如果箭头可以接受而不是sigil,则重新subtract
为
sub subtract {
my($x) = @_;
sub { my($y) = @_; $x - $y };
};
此案例中的调用类似于
my $result = subtract(40)->(20);
答案 2 :(得分:1)
请不要在程序上破坏语法扩展,以解决已解决的问题。 您想要的是闭包,以及有时称为 currying 的技术。
Currying是将一个函数转换为一个函数的工作,该函数将多个参数转换为多次调用的函数,每个函数使用一个参数。例如,考虑
sub subtract {
my ($x, $y) = @_;
return $x - $y;
}
现在我们可以创建一个已经提供第一个参数的子例程:
sub subtract1 { subtract(40, @_) }
调用subtract1(20)
现在评估为20
。
我们可以使用匿名子程序,这样可以更灵活:
my $subtract = sub { subtract(40, @_) };
$subtract->(20);
我们不需要那个变量:
sub { subtract(40, @_) }->(20); # equivalent to subtract(40, 20)
我们可以用直接执行此操作的方式编写subtract
:
sub subtract_curried {
my $x = shift;
# don't return the result, but a subroutine that calculates the result
return sub {
my $y = shift;
return $x - $y;
};
}
现在:subtract_curried(40)->(20)
- 注意两者之间的箭头,因为我们正在处理代码引用(匿名子程序或闭包的另一个名称)。
这种写作函数在 Haskell 或 OCaml 等函数式语言中更为常见,其语法更为漂亮。它允许非常灵活的功能组合。如果您对Perl中的这种编程感兴趣,可能需要阅读Higher-Order Perl。
答案 3 :(得分:0)
@Heartache:请忘记这个挑战,因为它对解析器和用户毫无意义。
您可以考虑使用fn[x][y]
或fn{x}{y}
作为有效的语法变体 - 即您可以堆叠[]
和{}
但不能列出,
或者看起来不错的fn(x,y)
或fn(x)->(y)
也是有效且有意义的语法变体。
但是fn(x)(y)
不知道应该在哪个上下文中使用第二个列表。
对于fn(x)(y)
,常见的解释是fn(x); (y) => (y)
。它在评估第一次调用后返回第二个列表。
答案 4 :(得分:-1)
您可以创建source code filter:
package BracketFilter;
use Filter::Util::Call;
sub import {
filter_add(sub {
my $status;
s/\)\(/, /g if ($status = filter_read()) > 0;
return $status ;
});
}
1;
并使用它:
#!/usr/bin/perl
use BracketFilter;
subtract(40)(20);
sub subtract {
return $_[0] - $_[1];
}