Ruby Language
Operatorzy
Szukaj…
Uwagi
Operatory to metody
Większość operatorów to tak naprawdę tylko metody, więc x + y
wywołuje metodę +
x
z argumentem y
, który zapisano by x.+(y)
. Jeśli napiszesz własną metodę mającą znaczenie semantyczne danego operatora, możesz zaimplementować swój wariant w klasie.
Jako głupi przykład:
# A class that lets you operate on numbers by name.
class NamedInteger
name_to_value = { 'one' => 1, 'two' => 2, ... }
# define the plus method
def + (left_addend, right_addend)
name_to_value(left_addend) + name_to_value(right_addend)
end
...
end
Kiedy stosować &&
vs. and
, ||
vs. or
Zauważ, że istnieją dwa sposoby wyrażania wartości logicznych: &&
lub and
oraz ||
lub or
- często są wymienne, ale nie zawsze. Będziemy nazywać je wariantami „znakowymi” i „słownymi”.
Warianty znaków mają wyższy priorytet, więc zmniejszenie potrzeby stosowania nawiasów w bardziej złożonych instrukcjach pomaga uniknąć nieoczekiwanych błędów.
Warianty słów pierwotnie miały być operatorem przepływu sterowania, a nie operatorem logicznym. Oznacza to, że zostały zaprojektowane do użycia w łańcuchowych instrukcjach metod:
raise 'an error' and return
Chociaż mogą być używane jako operatory logiczne, ich niższy priorytet sprawia, że są nieprzewidywalne.
Po drugie, wielu rubyistów preferuje wariant znaków podczas tworzenia wyrażenia boolowskiego (takiego, który ocenia na true
lub false
), takiego jak x.nil? || x.empty?
. Z drugiej strony, warianty słów są preferowane w przypadkach, w których analizuje się szereg metod i jedna z nich może zawieść. Na przykład wspólny idiom używający wariantu słowa dla metod zwracających nil
w przypadku niepowodzenia może wyglądać następująco:
def deliver_email
# If the first fails, try the backup, and if that works, all good
deliver_by_primary or deliver_by_backup and return
# error handling code
end
Priorytet operatora i metody
Od najwyższej do najniższej jest to tabela pierwszeństwa dla Ruby. Operacje o wysokim priorytecie mają miejsce przed operacjami o niskim priorytecie.
╔═══════════════════════╦════════════════════════════════════════╦═════════╗
║ Operators ║ Operations ║ Method? ║
╠═══════════════════════╬════════════════════════════════════════╬═════════╣
║ . ║ Method call (e.g. foo.bar) ║ ║
║ [] []= ║ Bracket Lookup, Bracket Set ║ ✓¹ ║
║ ! ~ + ║ Boolean NOT, complement, unary plus ║ ✓² ║
║ ** ║ Exponentiation ║ ✓ ║
║ - ║ Unary minus ║ ✓² ║
║ * / % ║ Multiplication, division, modulo ║ ✓ ║
║ + - ║ Addition, subtraction ║ ✓ ║
║ << >> ║ Bitwise shift ║ ✓ ║
║ & ║ Bitwise AND ║ ✓ ║
║ | ^ ║ Bitwise OR, Bitwise XOR ║ ✓ ║
║ < <= >= > ║ Comparison ║ ✓ ║
║ <=> == != === =~ !~ ║ Equality, pattern matching, comparison ║ ✓³ ║
║ && ║ Boolean AND ║ ║
║ || ║ Boolean OR ║ ║
║ .. ... ║ Inclusive range, Exclusive range ║ ║
║ ? : ║ Ternary operator ║ ║
║ rescue ║ Modifier rescue ║ ║
║ = += -= ║ Assignments ║ ║
║ defined? ║ Defined operator ║ ║
║ not ║ Boolean NOT ║ ║
║ or and ║ Boolean OR, Boolean AND ║ ║
║ if unless while until ║ Modifier if, unless, while, until ║ ║
║ { } ║ Block with braces ║ ║
║ do end ║ Block with do end ║ ║
╚═══════════════════════╩════════════════════════════════════════╩═════════╝
Unary + i unary - są dla +obj
, -obj
lub -(some_expression)
.
Modyfikator-jeśli, modyfikator-chyba, itp. Dotyczą wersji modyfikujących tych słów kluczowych. Na przykład jest to modyfikator, chyba że wyrażenie:
a += 1 unless a.zero?
Operatory z ✓ mogą być zdefiniowane jako metody. Większość metod jest nazywana dokładnie tak, jak nazwa operatora, na przykład:
class Foo
def **(x)
puts "Raising to the power of #{x}"
end
def <<(y)
puts "Shifting left by #{y}"
end
def !
puts "Boolean negation"
end
end
Foo.new ** 2 #=> "Raising to the power of 2"
Foo.new << 3 #=> "Shifting left by 3"
!Foo.new #=> "Boolean negation"
¹ W metodach wyszukiwania wspornika i zestawu wsporników ( []
i []=
) zdefiniowano argumenty po nazwie, na przykład:
class Foo
def [](x)
puts "Looking up item #{x}"
end
def []=(x,y)
puts "Setting item #{x} to #{y}"
end
end
f = Foo.new
f[:cats] = 42 #=> "Setting item cats to 42"
f[17] #=> "Looking up item 17"
² Operatory „unary plus” i „unary minus” są zdefiniowane na przykład jako metody o nazwach +@
i -@
class Foo
def -@
puts "unary minus"
end
def +@
puts "unary plus"
end
end
f = Foo.new
+f #=> "unary plus"
-f #=> "unary minus"
³ We wczesnych wersjach Ruby operator nierówności !=
I operator niepasujący !~
Nie mogły być zdefiniowane jako metody. Zamiast tego została wywołana metoda odpowiedniego operatora równości ==
lub operatora dopasowywania =~
, a wynik tej metody został odwrócony przez wartość logiczną Ruby.
Jeśli nie zdefiniujesz własnych operatorów !=
Lub !~
, Powyższe zachowanie jest nadal prawdziwe. Jednak od wersji Ruby 1.9.1 te dwa operatory można również zdefiniować jako metody:
class Foo
def ==(x)
puts "checking for EQUALITY with #{x}, returning false"
false
end
end
f = Foo.new
x = (f == 42) #=> "checking for EQUALITY with 42, returning false"
puts x #=> "false"
x = (f != 42) #=> "checking for EQUALITY with 42, returning false"
puts x #=> "true"
class Foo
def !=(x)
puts "Checking for INequality with #{x}"
end
end
f != 42 #=> "checking for INequality with 42"
Operator równości wielkości liter (===)
Znany również jako potrójny równa się .
Ten operator nie testuje równości, a raczej sprawdza, czy prawy operand ma relację IS A z lewym operandem. W związku z tym popularny operator równości nazw jest mylący.
Ta odpowiedź SO opisuje to w ten sposób: najlepszym sposobem na opisanie a === b
jest „jeśli mam szufladę oznaczoną a
, czy ma sens umieszczenie w niej b
?”. Innymi słowy, czy zestaw a
obejmuje element b
?
Przykłady ( źródło )
(1..5) === 3 # => true
(1..5) === 6 # => false
Integer === 42 # => true
Integer === 'fourtytwo' # => false
/ell/ === 'Hello' # => true
/ell/ === 'Foobar' # => false
Klasy, które zastępują ===
Wiele klas zastępuje ===
aby zapewnić znaczącą semantykę w instrukcjach case. Niektórzy z nich są:
╔═════════════════╦════════════════════╗
║ Class ║ Synonym for ║
╠═════════════════╬════════════════════╣
║ Array ║ == ║
║ ║ ║
║ Date ║ == ║
║ ║ ║
║ Module ║ is_a? ║
║ ║ ║
║ Object ║ == ║
║ ║ ║
║ Range ║ include? ║
║ ║ ║
║ Regexp ║ =~ ║
║ ║ ║
║ String ║ == ║
╚═════════════════╩════════════════════╝
Zalecana praktyka
Należy unikać jawnego użycia operatora równości wielkości ===
. Nie testuje równości, ale raczej subsumpcję , a jej użycie może być mylące. Kod jest jaśniejszy i łatwiejszy do zrozumienia, gdy zamiast niego używana jest metoda synonimiczna.
# Bad
Integer === 42
(1..5) === 3
/ell/ === 'Hello'
# Good, uses synonym method
42.is_a?(Integer)
(1..5).include?(3)
/ell/ =~ 'Hello'
Operator bezpiecznej nawigacji
Ruby 2.3.0 dodał bezpieczny operator nawigacji , &.
. Ten operator ma na celu skrócenie paradygmatu object && object.property && object.property.method
w instrukcjach warunkowych.
Na przykład masz obiekt House
z właściwością address
i chcesz znaleźć nazwę street_name
na podstawie address
. Aby bezpiecznie to zaprogramować, aby uniknąć błędów zero w starszych wersjach Rubiego, użyj kodu takiego jak ten:
if house && house.address && house.address.street_name
house.address.street_name
end
Operator bezpiecznej nawigacji skraca ten warunek. Zamiast tego możesz napisać:
if house&.address&.street_name
house.address.street_name
end
Uwaga:
Operator bezpiecznej nawigacji nie zachowuje się dokładnie tak samo jak łańcuch warunkowy. Korzystając z warunkowego łańcucha (pierwszy przykład), blok if
nie zostałby wykonany, jeśli powiedzmy, że address
był false
. Operator bezpiecznej nawigacji rozpoznaje tylko wartości nil
, ale dopuszcza takie wartości, jak false
. Jeśli address
jest false
, użycie SNO spowoduje błąd:
house&.address&.street_name
# => undefined method `address' for false:FalseClass