5

このような操作を実行する方法を知りたい

$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は後置演算子を追加できますか? もしそうなら、どのように?

4

3 に答える 3

3

オーバーロードを悪用して、必要なものに近づけることができます。

#!/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;

ご覧のとおり、"value" 属性と "type" 属性を持つオブジェクトが返されます。

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;

何か便利なことを実行できるようにするには、さらにメソッドを追加する必要があります。

ほとんどの場合、オーバーロードは、ソース フィルターと同様にパーティ トリックではありません。ほぼ確実に、プログラムがはるかに遅くなります。

于 2016-08-03T08:12:36.930 に答える