2016-08-02 22 views
5

Ich mag gerne wissen, wie ich Operationen wie diesePerl benutzerdefinierte Syntax für Suffixe oder benutzerdefinierte Postfix-Operatoren

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

laufen kann und sie kompilieren. Ich bin nicht wählerisch, was ihr Ergebnis ist oder wie es umgesetzt wird. Mein Ziel ist es, Ausdrücke für physikalische Größen mit konventionellen Postfix-Einheit-Annotationen zu Perl-Ausdrücken für diese Einheiten kompilieren zu lassen.

Ich denke, ich muss benutzerdefinierte Analysetechniken verwenden, aber ich würde lieber vorhandene Funktionalität oder Analysemodule über nur anwenden Regex-Filter auf meine rohe Quelle verwenden.

Parse::Keyword schien vielversprechend, aber ich kann nicht sehen, ob es Postfix-Operationen analysieren kann, und es behauptet, veraltet zu sein.

Edit: Ich möchte Quellenfilter vermeiden, wenn möglich, weil ich lieber nicht Regexes für Perl syntaktische Ecken Fällen schreiben würde (z. B. "25 (J/K)").

Der Fehler Perl hier erzeugt sagt:

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

Es scheint, wie ich in einzuhaken müssen, wo Perl erkennt Operatoren nach Zahlenliterale.

Konnte Devel::Declare Postfix-Operatoren hinzufügen? Wenn das so ist, wie?

+3

Klingt wie ein Job für einen [Quellfilter] (http://perldoc.perl.org/perlfilter.html). Verstehe, dass [Quellfilter fast immer eine schlechte Idee sind] (http://stackoverflow.com/questions/1785852/why-are-perl-source-filters-bad-and-when-is-it-ok- zu benutzen-sie). Ich würde einen Ansatz empfehlen, der kein Verschrauben mit Perls Parseregeln erfordert; es ist nicht so schön, aber ich würde etwas wie $ T = My :: Measurement-> new (Wert => 25, units => 'C'); '. – ThisSuitIsBlackNot

+0

Ziel dieses Moduls ist es, die knappe Form explizit wie eine domänenspezifische Sprache zuzulassen. – alexchandel

+0

Bitte beschreiben Sie genauer das Ergebnis, das Sie möchten. Ist $ T = '25 C'akzeptabel? – Borodin

Antwort

3

Sie können Überladung missbrauchen, um etwas zu bekommen, was Sie wollen.

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

Wie Sie sehen werden, erhalten Sie Objekte mit den Attributen "value" und "type" zurück.

MyUnits.pm sieht wie folgt aus:

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 [email protected]; 
    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; 

Sie wollen würde mehr Methoden hinzufügen, um es zu ermöglichen, etwas Nützliches zu tun.

In den meisten Fällen ist Überladen nicht viel weniger als ein Party-Trick als Quellfilter sind. Es wird fast sicher Ihr Programm viel langsamer machen.

0

Source-Filter sind notorisch spröde, aber wahrscheinlich der einfachste Weg, um zu bekommen, was Sie wollen, ohne tief in Perls weniger als appetitliche Eingeweide zu tauchen. So etwas wie dieses vielleicht:

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; 

Ich wollte es anal und änderte die „C“, wie es Ihnen Coulomb nicht bedeuten aussieht. Sie könnten wahrscheinlich obwohl °C alle use utf8 und schreiben gehen;)

Test:

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

Natürlich ist die einfache regexp viel zu wünschen übrig lässt, wie Klammern und so zu, für die Sie wahrscheinlich Text::Balanced brauchen würde, und Die Units Klasse könnte tatsächlich viel mehr wie die Analyse dieser Einheit Zeichenfolge und überladen ein paar Operatoren wollen, so dass Sie mit Einheiten berechnen können.

2

Wenn Sie bereit sind, eine vermittelnde Funktion zu verwenden, können Sie etwas bekommen, das wie das aussieht, was Sie wollen, wenn Sie hart genug schielen.Ich glaube nicht, ist es möglich ;-) Haskell in Perl zu schreiben

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(\[email protected]) { 
    my (undef, $value, $units) = @_; 
    ${ $_[0] } = PV "$value $units"; 
    return; 
} 

__PACKAGE__; 
__END__ 

Verwenden Sie es aus einem Skript:

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

Ausgang:

C:\...\t> perl t.pl 
97 F 
-6.67 C

Nun Math::Units::PhysicalValue bedingungslos verwendet Math::BigFloat Die Arithmetik sollte langsam, aber genau sein. Wenn Sie diese Art von Sache wirklich brauchen, sollten Sie in die aufräumenden Teile von Math::Units und Math::Units::PhysicalValue schauen und etwas schneller von diesen Teilen schaffen.