How to convert any method to infix operator in ruby
A bit late to the party but I've been toying around with it and you can use operator overloading to create Infix operators just like in python (but with a bit more work), the syntax becomes a |op| b
, here's how:
First a quick and dirty copy-paste to play around with 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;if@c;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
Step 1: create the Infix
class
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
Step 2: fix all the classes that don't have a |
method and the three special cases (true
, false
, and nil
) (note: you can add any class in here and it will probably work fine)
[ 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}}
Step 3: define your operators in one of 5 ways
# 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
Step 4: use them (spacing doesn't matter):
2 |pow| 3 # => 8
9|mod|4 # => 1
3| avg |6 # => 4.5
0 | pick | 1 # => 0 or 1 (randomly chosen)
You can even kinda sorta curry:
(This only works with the first operand)
diff_from_3 = 3 |diff
diff_from_3| 2 # => 1
diff_from_3| 4 # => 1
diff_from_3| -3 # => 6
As a bonus, this little method allows you to define Infixes (or any object really) without using .new
:
def Infix *args, &block
Infix.new *args, &block
end
pow = Infix -> (x, y) {x ** y} # and so on
All that's left to do is wrap it up in a module
Hope this helped
P.S. You can muck about with the operators to have something like a <<op>> b
, a -op- b
, a >op> b
and a <op<b
for directionality, a **op** b
for precedence and any other combination you want but beware when using true
, false
and nil
as the first operand with logical operators (|
, &&
, not
, etc.) as they tend to return before the infix operator is called.
For example: false |equivalent_of_or| 5 # => true
if you don't correct.
FINALLY, run this to check a bunch of cases of all the builtin classes as both the first and second operand:
# 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}
Define custom Ruby operator
Yes, custom operators can be created, although there are some caveats. Ruby itself doesn't directly support it, but the superators gem does a clever trick where it chains operators together. This allows you to create your own operators, with a few limitations:
$ gem install superators19
Then:
require 'superators19'
class Array
superator "%~" do |operand|
"#{self} percent-tilde #{operand}"
end
end
puts [1] %~ [2]
# Outputs: [1] percent-tilde [2]
Due to the aforementioned limitations, I couldn't do your 1 %! 2
example. The Documentation has full details, but Fixnums can't be given a superator, and !
can't be in a superator.
Passing an operator to a function?
Two possibilities:
Take method/operator name as a symbol:
def sum a,b,operator
a.send(operator, b)
end
sum 42, 23, :+
Or the more general solution: Take a block:
def sum a,b
yield a,b
end
sum 42, 23, &:+
Defining a new logical operator in Ruby
Without editing the Ruby parser and sources and compiling a new version of Ruby, you can't. If you want, you can use this ugly syntax:
class Object
def but(other)
self and other
end
end
x.but (not y)
Note that you can't remove the parentheses or the space in this snippet. It will also shadow the functionality of the code to someone else reading your code. Don't do it.
Is == a special method in Ruby?
No, ==
is not a special method in Ruby. It's a method like any other. What you are seeing is simply a parsing issue:
a.x foo 4
is the same as
a.x(foo(4))
IOW, you are passing foo(4)
as an argument to x
, but x
doesn't take any arguments.
There is, however, special operator syntax, which allows you to write
a == b
instead of
a.== b
for a limited list of operators:
==
!=
<
>
<=
>=
<=>
===
&
|
*
/
+
-
%
**
>>
<<
!==
=~
!~
Also, there is special syntax that allows you to write
!a
and
~a
instead of
a.!
and
a.~
As well as
+a
and
-a
instead of
a.+@
and
a.-@
Then, there is
a[b]
and
a[b] = c
instead of
a.[] b
and
a.[]= b, c
and last but not least
a.(b)
instead of
a.call b
how can i rename a operator method with words, respecting sintax in ruby?
Ruby has particular operators you can override, like %
, +
, and &
, but you can't just invent arbitrary operators on a whim. You need to work with pre-existing ones.
This is a function of how the Ruby parser works. It can only identify a pre-defined set of symbols outside of regular method calls.
Trait.new.with x
is a method call, equivalent to Trait.new.send(:with, x)
, while Trait.new with x
is Trait.new(with(x))
which is not what you want.
Your alias
creates a method, it does not create an operator. You cannot create a brand-new operator.
You're going to have to decide between the two forms x & y
vs. x.with y
.
If statement operator as a variable in ruby
Try this one
x = 0
y = 1
z = "!="
x.public_send(z, y)
=> true
The trick here is know that 3 >= 5
is syntactic sugar for 3.>=(5)
Convert postgres hstore sql to Arel
You can accomplish this with Arel by creating your own Arel::Nodes::InfixOperation:
ut = Arel::Table.new(:user)
hstore_key = Arel::Nodes::InfixOperation.new("->", ut[:properties], 'is_robber')
User.where(hstore_key.eq('true'))
Will produce:
SELECT "users".* FROM "users" WHERE "users"."properties" -> 'is_robber' = 'true'
If you've never heard of infix notation, Wikipedia gives a good explaination:
Infix notation is the common arithmetic and logical formula notation, in which operators are written infix-style between the operands they act on (e.g. 2 + 2). It is not as simple to parse by computers as prefix notation ( e.g. + 2 2 ) or postfix notation ( e.g. 2 2 + ), but many programming languages use it due to its familiarity.
Unfortunately, there's not much documentation for Arel, and none for the InfixOperation node, so it can be frustrating to start out with. When you're looking for how to perform particular SQL operations with Arel, your best bet is to look through the nodes directory in the source code.
Related Topics
Version Sort (With Alphas, Betas, etc.) in Ruby
Converting a Unique Seed String into a Random, Yet Deterministic, Float Value in Ruby
Rails Cannot Load Such File -- MySQL2/Mysql2 (Loaderror)
Wrapping Text into Lines at Word Boundaries
Keyword Arguments Unpacking (Splat) in Ruby
Encoding::Undefinedconversionerror: "\Xe4" from Ascii-8Bit to Utf-8
Working with Decimals in Ruby on Rails 3
Rbenv Install Ruby Build Failed
How to Find Current Abstract Route in Rails Middware
Use Ruby Array for a JavaScript Array in Erb. Escaping Quotes
Ruby on Rails Source Code Security/Obfuscation
Scanning for Unicode Numbers in a String with \D
-': Nil Can't Be Coerced into Fixnum (Typeerror)
Can't Get to Work Cocoapods and Yosemite
In Ruby, What Are the Vertical Lines