2016-11-30 4 views
1

In einigen Sprachen wie Haskell, it is possible, um eine beliebige Funktion zu verwenden, die zwei Argumente als Infix-Operator.So konvertieren Sie eine Methode zu Infix-Operator in Ruby

Ich finde diese Notation interessant und möchte das gleiche in Rubin erreichen.

eine imaginäre Methode or_if_familiar Da ich wie "omg" or_if_familiar "oh!" etwas schreiben zu können, möchte statt or_if_familiar("omg", "oh!")

Wie man eine solche Notation in Ruby (ohne Änderung Rubin selbst) schaffen würde?

+0

ich jeden Rubin Beispiel nicht finden können, aber ich gebe nicht zu schreiben, versucht zu haben, Code auf meiner Seite noch – kamaradclimber

+1

Da dies eine Eigenschaft des _parser_ ist, ist es nicht möglich. – mudasobwa

Antwort

3

Ein bisschen spät zur Party, aber ich habe mit ihm wurden Herumspielen und Sie können eine Überlastung verwenden Operator Infixoperatoren nur erstellen wie in python (aber mit ein bisschen mehr Arbeit), wird die Syntax a |op| b, hier ist wie:

Zuerst eine schnelle und schmutzige Kopie-Paste um mit Infix zu spielen:

class Infix def initialize*a,&b;raise'arguments size mismatch'if a.length<0||a.length>3;raise'both method and b passed'if a.length!=0&&b;raise'no arguments passed'if a.length==0&&!b;@m=a.length>0? a[0].class==Symbol ? method(a[0]):a[0]:b;if a.length==3;@c=a[1];@s=a[2]end end;def|o;[email protected];o.class==Infix ? self:@m.(@s,o)else;raise'missing first operand'end end;def coerce o;[Infix.new(@m,true,o),self]end;def v o;Infix.new(@m,true,o)end end;[NilClass,FalseClass,TrueClass,Object,Array].each{|c|c.prepend Module.new{def|o;o.class==Infix ? o.v(self):super end}};def Infix*a,&b;Infix.new *a,&b end 
# 

Ok

Schritt 1: Erstellen Sie die Infix Klasse

class Infix 
    def initialize *args, &block 
    raise 'error: arguments size mismatch' if args.length < 0 or args.length > 3 
    raise 'error: both method and block passed' if args.length != 0 and block 
    raise 'error: no arguments passed' if args.length == 0 and not block 
    @method = args.length > 0 ? args[0].class == Symbol ? method(args[0]) : args[0] : block 
    if args.length == 3; @coerced = args[1]; @stored_operand = args[2] end 
    end 
    def | other 
    if @coerced 
     other.class == Infix ? self : @method.call(@stored_operand, other) 
    else 
     raise 'error: missing first operand' 
    end 
    end 
    def coerce other 
    [Infix.new(@method, true, other), self] 
    end 
    def convert other 
    Infix.new(@method, true, other) 
    end 
end 

Schritt 2: alle Klassen beheben, die keine | Verfahren und die drei Sonderfälle haben (true, false und nil) (Anmerkung: Sie können jede Klasse hier hinzufügen und es wird wahrscheinlich gut funktionieren)

[ NilClass, FalseClass, TrueClass, 
    Float, Symbol, String, Rational, 
    Complex, Hash, Array, Range, Regexp 
].each {|c| c.prepend Module.new { 
    def | other 
    other.class == Infix ? other.convert(self) : super 
    end}} 

Schritt 3: Ihre Betreiber in einem der 5 Arten definieren

# Lambda 
pow = Infix.new -> (x, y) {x ** y} 
# Block 
mod = Infix.new {|x, y| x % y} 
# Proc 
avg = Infix.new Proc.new {|x, y| (x + y)/2.0} 
# Defining a method on the spot (the method stays) 
pick = Infix.new def pick_method x, y 
    [x, y][rand 2] 
end 
# Based on an existing method 
def diff_method x, y 
    (x - y).abs 
end 
diff = Infix.new :diff_method 

Schritt 4: verwenden sie (Abstand spielt keine Rolle):

2 |pow| 3  # => 8 
9|mod|4  # => 1 
3| avg |6  # => 4.5 
0 | pick | 1 # => 0 or 1 (randomly chosen) 

Sie können sogar ein bisschen sorta Curry: (Dies funktioniert nur mit dem ersten Operanden)

Als Bonus können Sie diese kleine Methode definieren Infixe (oder irgendein Objekt wirklich) ohne Verwendung von .new:

def Infix *args, &block 
    Infix.new *args, &block 
end 

pow = Infix -> (x, y) {x ** y} # and so on 

Alles, was es zu tun übrig bleibt, wickeln in einem Modul bis

Hope this half

P. S. Sie können mit den Betreibern Dreck über so etwas wie a <<op>> b zu haben, a -op- b, a >op> b und a <op<b für Direktionalität, a **op** b für Vorrang und andere Kombination Sie wollen, aber Vorsicht, wenn true verwenden, false und nil als erster Operand mit logischen Operatoren (|, && , not, usw.), da sie dazu neigen, zurückzukehren, bevor der Infix-Operator aufgerufen wird.

Zum Beispiel: false |equivalent_of_or| 5 # => true wenn Sie nicht korrigieren.

ENDLICH, diese laufen eine Reihe von Fällen aller eingebauten Klassen als sowohl der ersten und zweiten Operanden zu überprüfen:

# pp prints both inputs 
pp = Infix -> (x, y) {"x: #{x}\ny: #{y}\n\n"} 

[ true, false, nil, 0, 3, -5, 1.5, -3.7, :e, :'3%4s', 'to', 
    /no/, /(?: [^A-g7-9]\s)(\w{2,3})*?/, 
    Rational(3), Rational(-9.5), Complex(1), Complex(0.2, -4.6), 
    {}, {e: 4, :u => 'h', 12 => [2, 3]}, 
    [], [5, 't', :o, 2.2, -Rational(3)], (1..2), (7...9) 
].each {|i| puts i.class; puts i |pp| i} 
2

In Ruby wird durch den Parser festgelegt, ob der Operator Präfix oder Infix ist. Die Vorrangstellung des Operators ist ebenfalls festgelegt. Es gibt keine Möglichkeit, den Parser zu ändern, diese Dinge zu ändern.

Aber Sie können die integrierten Operatoren für Ihre Objekte

implementieren Obwohl Sie das Update-ness oder Vorrang eines eingebauten Operator nicht ändern können, können Sie Ihre Objekte Operatoren durch die Definition von Methoden implementieren können. Das liegt daran, dass Ruby Operatoren in Methodenaufrufe übersetzt. Zum Beispiel dieser Ausdruck:

a + b 

in übersetzt:

a.+(b) 

Daher können Sie den Operator + für ein beliebiges Objekt implementieren, indem Sie die + Methode definieren:

def +(rhs) 
    ... 
end 

Die Präfix-Operator - verursacht einen Aufruf der Methode @-, so zu implementieren Präfix - Sie tun dies:

def @- 
    .. 
end 

Sie können auch Methoden

Sie können Ihren eigenen Infixoperatoren als einfacher Methoden implementieren. Dies erfordert eine etwas andere Syntax als gewünscht. Sie möchten:

"omg" or_if_familiar "oh!" 

Was Sie nicht haben können. Was kann man haben, ist:

"omg".or_if_familiar "oh!" 

Das funktioniert, weil, in Ruby, die Klammern auf Methodenargumente oft weggelassen werden kann. Die oben ist äquivalent zu:

"omg".or_if_familiar("oh!") 

In diesem Beispiel würden wir dies durch Affen-Patchen der String-Klasse implementieren:

class String 
    def or_ir_familiar(rhs) 
    ... 
    end 
end 
0

Rubin nicht Infix Methode Syntax hat, mit Ausnahme eines festen und vordefinierten Reihe von Operatoren. Und Ruby erlaubt dem Benutzercode nicht, die Sprachsyntax zu ändern. Ergo, was du willst ist nicht möglich.

0

Basierend auf Wayne Conrad ‚s Antwort, kann ich den folgenden Code schreiben, die in Ruby Top-Level definiert für jede Methode funktionieren würde:

class Object 
    def method_missing(method, *args) 
    return super if args.size != 1 
    # only work if "method" method is defined in ruby top level 
    self.send(method, self, *args) 
    end 
end 

die

def much_greater_than(a,b) 
    a >= b * 10 
end 

"A very long sentence that say nothing really but should be long enough".much_greater_than "blah" 

# or 

42.much_greater_than 2 

Dank Wayne schreiben können !

Interessante Referenz auf dem gleichen Thema:

Verwandte Themen