2013-12-18 55 views
0

我一直在使用perl一段时间。 我想知道我怎么能在Perl中运行以下操作:用于传递函数参数的Perl自定义语法

subtract(40)(20) 

为了得到结果:

20 

我想我会看看自定义分析技术为Perl。 这是我在看现在:

Devel::Declare

Devel::CallParser

http://www.perl.com/pub/2012/10/an-overview-of-lexing-and-parsing.html

现在,我不知道要寻找什么或做什么。 如何解决这个问题的任何帮助,我们将不胜感激。请清楚。 谢谢。

+2

你想做什么? *看起来像是在试图使用[函数指针](http://mpechner.wordpress.com/2008/04/11/perl-tutorial-function-pointers/)。 –

+0

我希望能够以上述方式运行一个名为“减”的子程序。 – Heartache

+2

'subtract(40) - >(20)'将是有效的语法。如果'subtract'返回闭包,它甚至可以工作。 '(40)(20)'不会有效,除非你对解析器做了严重的暴力行为。而且,除了大约幼儿园以来我们都知道的优秀的旧中缀“ - ”,这两种替代方案都不是一种更易读的减法算子。有没有一点意义? –

回答

2

如果你可以用一个印记的增加和箭头生活,你可以currysubtract

my $subtract = sub { 
    my($x) = @_; 

    sub { my($y) = @_; $x - $y }; 
}; 

说它在

my $result = $subtract->(40)(20); 

如果箭头是可以接受的,但不是sigil,重铸subtract as

sub subtract { 
    my($x) = @_; 

    sub { my($y) = @_; $x - $y }; 
}; 

在这种情况下的调用看起来像

my $result = subtract(40)->(20); 
+0

如果你在'(40)'的左边有一个,你会发现''(40)'和'(20)'之间不需要' - >'。令人害怕的有趣...... –

+0

@ WumpusQ.Wumbley在[perlref](http://perldoc.perl.org/perlref.html#Using-References)的“Using References”一节中,我们读到“The arrow在*括号下标之间是可选的*“ –

+0

嗯......在我的世界部分(也是perl及其文档来自的世界的一部分)中,”括号“并不意味着”括号“。但我看到这个例子还包括了大括号,我不认为它们是括号。但'$ array [$ x] {foo} [0]'在任何地方都没有箭头是有效的,而'subtract(40)(20)'至少需要一个箭头*。在我看来,“可选箭头”规则对函数调用括号的应用不同于对数组查找括号或散列查找大括号的应用。 –

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) - 注意之间的箭头,因为我们正在处理代码引用(匿名子例程的另一个名称或闭包)。

的书写功能这种风格函数式语言更为普遍喜欢哈斯克尔OCaml的其中,此语法是漂亮。它允许非常灵活的功能组合。如果您对Perl中的这种编程感兴趣,您可能需要阅读Higher-Order Perl

+0

我需要这样的电话:减(40)(20) 我的感谢您的回应。 – Heartache

+0

@Heartache当你有这样严格的要求时,你真的试图解决什么问题?如果你确实需要写一个语法扩展,至少应该使用'Parse :: Keywords'而不是其中一个较老的(更多的)实验。 – amon

+0

这是一个挑战问题。 – Heartache

3

我推荐尝试Parse::Keyword。 Parse :: Keyword非常适合解析自定义语法,因为它允许您回拨Perl解析器的各个部分,如parse_listexprparse_block,parse_fullstmt等(请参阅perlapi)。

它有一个缺点,如果你使用它们来解析关闭变量的表达式,这些处理很糟糕,但是这可以用PadWalker解决。

Parse :: Keyword(包括PadWalker的诡计)是Kavorka的用途;而且这会做一些非常复杂的事情!早期版本的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

您可以创建的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]; 
} 
+0

源代码过滤器是一个错误。不要使用它们来实现语法扩展 - 大多数情况下,'Parse :: Keyword'是更好的解决方案。 – amon

+0

@amon,参见['Parse :: Keyword'](http://metacpan.org/pod/release/DOY/Parse-Keyword-0.08/lib/Parse/Keyword.pm):_DEPRECATED:在perl中编写语法扩展。请勿使用!_ –

+0

所有语法扩展模块在某种程度上都被破坏 - [tobyink对此问题的回答](http://stackoverflow.com/a/20669696/1521179)显示了如何克服Parse :: Keyword限制。这个模块仍然是直接使用[Perl的C API](http://perldoc.perl.org/perlapi.html)的最佳选择。 – amon

0

@Heartache:请忘记这个挑战,因为它对解析器和用户没有意义。

您可以考虑使用fn[x][y]fn{x}{y}它们是有效的语法变种 - 即可以叠加[]{}但不是列表, 或fn(x,y)fn(x)->(y)这也好看,也是有效的和有意义的语法变种。 但fn(x)(y)将不知道在哪个上下文中应该使用第二个列表。

对于fn(x)(y)的常见解释是fn(x); (y) => (y)。它在评估第一次调用后返回第二个列表。

相关问题