传递函数参数的Perl自定义语法

传递函数参数的Perl自定义语法,perl,parsing,lexical,Perl,Parsing,Lexical,我使用perl已经有一段时间了。 我想知道如何在perl中运行以下操作: subtract(40)(20) 要获得结果,请执行以下操作: 20 我想我必须看看Perl的自定义解析技术。 这就是我现在看到的: 及 现在,我不知道该找什么或做什么。 任何关于如何进行这方面的帮助,阅读内容将不胜感激。请讲清楚。 谢谢。如果你能忍受一个符号和一个箭头的增加,你可以减去 my $subtract = sub { my($x) = @_; sub { my($y) = @_; $x -

我使用perl已经有一段时间了。 我想知道如何在perl中运行以下操作:

subtract(40)(20)
要获得结果,请执行以下操作:

20
我想我必须看看Perl的自定义解析技术。 这就是我现在看到的:

现在,我不知道该找什么或做什么。 任何关于如何进行这方面的帮助,阅读内容将不胜感激。请讲清楚。
谢谢。

如果你能忍受一个符号和一个箭头的增加,你可以
减去

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

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

my $result = $subtract->(40)(20);
如果箭头可接受,但符号不可接受,则根据需要重铸
subtract

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

  sub { my($y) = @_; $x - $y };
};
本例中的调用如下所示

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

如果你能忍受一个符号和一个箭头的增加,你可以像在

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

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

my $result = $subtract->(40)(20);
如果箭头可接受,但符号不可接受,则根据需要重铸
subtract

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

  sub { my($y) = @_; $x - $y };
};
本例中的调用如下所示

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

请不要为了解决问题而在程序上附加不正确的语法扩展。 您需要的是闭包,以及一种有时称为curry的技术

curry是将一个包含多个参数的函数转换为一个函数的工作,该函数使用一个参数多次调用。例如,考虑

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)
我们可以直接写
减法

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


这种编写函数的风格在Haskell或OCaml等函数式语言中更为常见,因为它们的语法更为美观。它允许非常灵活的功能组合。如果您对Perl中的这种编程感兴趣,您可能需要阅读。

请不要在程序上附加不正确的语法扩展来解决已解决的问题。 您需要的是闭包,以及一种有时称为curry的技术

curry是将一个包含多个参数的函数转换为一个函数的工作,该函数使用一个参数多次调用。例如,考虑

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)
我们可以直接写
减法

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

这种编写函数的风格在Haskell或OCaml等函数式语言中更为常见,因为它们的语法更为美观。它允许非常灵活的功能组合。如果您对这种Perl编程感兴趣,可能需要阅读。

我建议您尝试一下。Parse::Keyword对于解析自定义语法非常有用,因为它允许您回调Perl解析器的各个部分,例如
Parse_listexpr
Parse_block
Parse_fullstmt
,等等(请参阅)

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

Parse::关键字(包括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
我建议你试试。Parse::Keyword对于解析自定义语法非常有用,因为它允许您回调Perl解析器的各个部分,例如
Parse_listexpr
Parse_block
Parse_fullstmt
,等等(请参阅)

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

Parse::关键字(包括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
您可以创建:

并使用它:

#!/usr/bin/perl

use BracketFilter;

subtract(40)(20);

sub subtract {
    return $_[0] - $_[1];
}
您可以创建:

并使用它:

#!/usr/bin/perl

use BracketFilter;

subtract(40)(20);

sub subtract {
    return $_[0] - $_[1];
}

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

您可以考虑使用
fn[x][y]
fn{x}{y}
,这是有效的语法变体,即您可以堆叠
[]
{}
,但不能堆叠列表, 或者
fn(x,y)
或者
fn(x)->(y)
这些看起来不错的语法变体也是有效且有意义的语法变体。 但是
fn(x)(y)
将不知道第二个列表应该在哪个上下文中使用


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

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

您可以考虑使用
fn[x][y]
fn{x}{y}
,这是有效的语法变体,即您可以堆叠
[]
{}
,但不能堆叠列表, 或者
fn(x,y)
或者
fn(x)->(y)
这些看起来不错的语法变体也是有效且有意义的语法变体。 但是
fn(x)(y)
将不知道第二个列表应该在哪个上下文中使用


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

您想做什么?那个lo