2016-11-30 21 views
1

ハスケルのような言語では、it is possibleは、中置演算子として2つの引数をとる関数を使用します。ルビの演算子を中置演算子に変換する方法

私はこの表記が面白いと思うし、ルビーでも同じことを達成したいと思います。

私は1つが(ルビー自体を変更せずに)ルビーで、このような表記を作成する方法を代わりにor_if_familiar("omg", "oh!")

"omg" or_if_familiar "oh!"ような何かを書くことができるようにしたいと思い虚方法or_if_familiar を考えると?

+0

最後に、第1および第2のオペランドの両方として、すべての組み込みクラスの例束を確認するために、これを実行します私の側のコードはまだ – kamaradclimber

+1

これは_parser_の機能なので不可能です。 – mudasobwa

答えて

3

ビットパーティーに遅れてが、私はそれを周りいじるしてきたあなただけの中置演算子を作成するために、演算子オーバーロードを使用することができますpythonのように(ただし、もう少し作業が必要です)、構文はになります。

最初に、すばやく汚れたコピーを貼り付けてInfixで再生します:

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]を

ステップ1:Infixクラスを作成

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 

ステップ2:|方法と3つの特別な場合(trueを持っていないすべてのクラスを修正し、falsenil)(注:ここに任意のクラスを追加することができます。これはおそらく正常に動作します)

[ 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}} 

ステップ3:それらを使用する(間隔は関係ありません)::5のいずれかの方法

# 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 

ステップ4であなたの演算子を定義

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

あなたもちょっとみかんカレーことができます。 を(これは最初のオペランドでのみ機能します)

diff_from_3 = 3 |diff 

diff_from_3| 2 # => 1 
diff_from_3| 4 # => 1 
diff_from_3| -3 # => 6 

この小さな方法では、 .newを使用せずに挿入辞(または実際に任意のオブジェクト):

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

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

すべてのことを行うに残って、これは

P.S.を助けた

希望のモジュールでそれを包むありますあなたが優先としたい任意の他の組み合わせのためにa **op** b、方向性のためにa <<op>> ba -op- ba >op> ba <op<bのようなものを持っている事業者とおよそマックが、論理演算子(|&&との最初のオペランドとしてtruefalsenilを使用する際に注意することができます、notなど)を返します。

たとえば、訂正しないとfalse |equivalent_of_or| 5 # => trueとなります。私はルビーの例を見つけることができませんが、私はいずれかを記述しようとしたではない認める

# 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

Rubyでは、演算子が接頭辞か中置かにかかわらず、パーサによって固定されています。オペレータの優先順位も固定されています。パーサーを変更してこれらの変更を行う方法がありません。

しかし、あなたは組み込みの演算子の修正ネスや優先度を変更することはできませんが、あなたがメソッドを定義することによって、あなたのオブジェクトのための演算子を実装することができ、あなたのオブジェクトに対して

を組み込み演算子を実装することができます。これは、Rubyが演算子をメソッド呼び出しに変換するためです。たとえば、この式:したがって、あなたは+メソッドを定義することにより、任意のオブジェクトの+演算子を実装することができる

a.+(b) 

def +(rhs) 
    ... 
end 

a + b 

が翻訳されます接頭辞演算子-は、メソッド@-を呼び出すため、接頭辞を実装するには -

def @- 
    .. 
end 

また、あなたはプレーンな方法として、独自の中置演算子を実装することができる方法

を使用することができます。これはあなたが望むものとは少し異なる構文を必要とします。あなたは欲しい:

"omg" or_if_familiar "oh!" 

あなたが持っていることができます。何が持つことができることです:Rubyで、メソッドの引数の括弧は、多くの場合、省略することができ、これは

"omg".or_if_familiar "oh!" 

動作します。上記と同じです。この例では

"omg".or_if_familiar("oh!") 

、我々は猿パッチングStringクラスでこれを実装します:

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

Rubyは固定と事前に定義された以外、中置メソッドの構文を持っていません演算子のセット。 Rubyでは、ユーザーコードで言語の構文を変更することはできません。エルゴ、あなたが望むものは不可能です。

0

Wayne Conradの回答に基づいて、私はRubyで定義された任意の方法のために働くだろう次のコードトップレベルを書くことができますが:

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 

おかげでウェインを書くことができます

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 

同じテーマに関する興味深い参照: