【问题标题】:Perl custom syntax for suffixes or custom postfix operators后缀或自定义后缀运算符的 Perl 自定义语法
【发布时间】:2016-08-04 01:23:43
【问题描述】:

我想知道如何运行这样的操作

$T = 25 C;
@specs = (273.15 K, 23 bar, 2.0 mol/s);

并让它们编译。我对他们的结果是什么或如何实施并不挑剔。我的目标是让带有传统后缀单位注释的物理量表达式编译为这些单位的 perl 表达式。

我认为我需要使用自定义解析技术,但我更愿意使用任何现有功能或解析模块,而不是仅将正则表达式过滤器应用于我的原始源。

Parse::Keyword 看起来很有希望,但我看不到它是否可以解析后缀操作,并且它声称已弃用。

编辑:如果可能,我想避免使用源过滤器,因为我不想为 Perl 的语法极端情况(例如“25 (J/K)”)编写正则表达式。

Perl 在这里产生的错误说明:

perl -E "25 C"
Bareword found where operator expected at -e line 1, near "25 C"
(Missing operator before C?)

看来我需要挂钩 Perl 在数字文字之后检测运算符的位置。

Devel::Declare 可以添加后缀运算符吗?如果有,怎么做?

【问题讨论】:

  • 听起来像是source filter 的工作。不过,请理解source filters are almost always a bad idea。我会推荐一种不需要使用 perl 的解析规则的方法;它不是那么漂亮,但我更喜欢$T = My::Measurement->new(value => 25, units => 'C'); 之类的东西。
  • 这个模块的目标是明确地允许简洁的形式,就像一种特定领域的语言。
  • 请更准确地描述您想要的结果。 $T = '25 C' 可以接受吗?
  • 没有。我的目标是不引用这些表达式并在以后手动解析它们,而是教 perl 编译器(或破解 perl 的解析)使其能够自己解析这些后缀表达式。
  • 你觉得写$T = 25 .C可以接受吗?这个想法是(ab)使用运算符重载来构造对象。概念证明here.

标签: perl parsing postfix-operator


【解决方案1】:

你可以滥用重载来获得接近你想要的东西。

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use Data::Dumper;
use MyUnits;

my $T = '25 C';

say Dumper $T;

my @specs = ('273.15 K', '23 bar', '2.0 mol/s');

say Dumper \@specs;

如您所见,您会返回带有“值”和“类型”属性的对象。

MyUnits.pm 如下所示:

package MyUnits;

use strict;
use warnings;

use overload
  '""' => \&to_string;

my %_const_handlers = (
  q => \&string_parser,
);

sub string_parser {
  my $c = eval { __PACKAGE__->new($_[0]) };
  return $_[1] if $@;
  return $c;
}

sub import {
  overload::constant %_const_handlers;
}

sub new {
  my $class = shift;

  # ->new(type => ..., value => ...)
  if (@_ == 4) {
    return bless { @_ }, $class;
  }
  # ->new({ type => ..., value => ...)
  if (@_ == 1 and ref $_[0] eq 'HASH') {
    return bless $_[0], $class;
  }
  # -> new('999xxx')
  if (@_ == 1 and ! ref $_[0]) {
    my ($val, $type) = $_[0] =~ /(\d+\.?\d*)\s*(.+)/;
    return bless({
      value => $val, type => $type,
    });
  }
}

sub to_string {
  return "$_[0]->{value}$_[0]->{type}";
}

1;

您希望添加更多方法以使其能够做一些有用的事情。

在大多数情况下,重载并不比源过滤器少多少。它几乎肯定会让你的程序慢得多。

【讨论】:

    【解决方案2】:

    如果您愿意使用中介功能,只要您足够用力地眯起眼睛,您就可以获得您想要的东西。我不可能用 Perl 编写 Haskell ;-)

    package My::Units;
    
    use strict;
    use warnings;
    
    use Importer 'Math::Units::PhysicalValue', 'PV';
    
    our @EXPORT = qw();
    our @EXPORT_OK = qw( with_units );
    
    sub with_units(\$@) {
        my (undef, $value, $units) = @_;
        ${ $_[0] } = PV "$value $units";
        return;
    }
    
    __PACKAGE__;
    __END__
    

    从脚本中使用它:

    #!/usr/bin/env perl
    
    use feature 'say';
    use strict;
    use warnings;
    
    use lib '.';
    use Importer 'My::Units', 'with_units';
    
    with_units my $x => 25 => 'C';
    with_units my $y => 20 => 'F';
    with_units my $z =>  0 => 'C';
    
    say $x + $y;
    say $y + $z;
    

    输出:

    C:\...\t> perl t.pl
    97楼
    -6.67℃

    现在,Math::Units::PhysicalValue 无条件使用Math::BigFloat,所以算术应该很慢,但准确。如果你真的需要这种东西,你可能想研究一下清理 Math::UnitsMath::Units::PhysicalValue 的部分,并从这些部分中更快地创建一些东西。

    【讨论】:

      【解决方案3】:

      源过滤器是出了名的脆弱,但可能是获得所需内容的最简单方法,而无需深入研究 perl 的不足之处。可能是这样的:

      package U;
      use strict;
      use warnings;
      use Filter::Simple;
      
      my @UNITS = qw( degC degK bar mol s );
      
      FILTER {
          my $unit_re = '(?:' . join('|', @UNITS) . ')';
          s#(\d+(?:\.\d\+)?)\s?((?:${unit_re}[*/])*$unit_re)\b#Units->new({value => $1, unit => '$2'})#g;
      };
      
      package Units;
      use Class::Accessor 'antlers';
      has value => ( is => "ro", isa => "Num" );
      has unit => ( is => "ro", isa => "Str" );
      
      1;
      

      我对此很生气,并更改了“C”,因为它看起来不是库仑。不过,您可能会全部使用use utf8 并写°C ;)

      测试:

      perl -I. -MU -e'my $val = 23 degK/s; printf "Value: %g, Unit: %s\n", $val->value, $val->unit'
      Value: 23, Unit: degK/s
      

      当然,简单的正则表达式还有很多不足之处,比如括号之类的你可能需要Text::Balanced,而Units 类实际上可能更像是解析那个单元字符串和重载一些运算符,以便您可以使用单位进行计算。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-22
        • 1970-01-01
        • 1970-01-01
        • 2015-08-02
        • 2019-04-08
        • 1970-01-01
        相关资源
        最近更新 更多