Что означает как операторы *, так и (*) в рубине?
Я только что представил себя в Ruby splat oprator. И я много играл с ним. но приведенный ниже эксперимент каким-то образом заставил меня
подумайте об этом дважды:)
langs = ["java", "csharp", "ruby", "haskell" ]
# => ["java", "csharp", "ruby", "haskell"]
l1,*,l2 = *langs
# => ["java", "csharp", "ruby", "haskell"]
l1
# => "java"
l2
# => "haskell"
l1,*,*,l2 = *langs
SyntaxError: (irb):27: syntax error, unexpected tSTAR
l1,*,*,l2 = *langs
^
from /usr/bin/irb:12:in `<main>'
Да, ошибка очевидна, так как я использовал более 1 оператора *(splat)
в том же списке аргументов.
Теперь я попытался сыграть с ним.
l1,(*),(*),l2 = *langs
# => ["java", "csharp", "ruby", "haskell"]
Аааа! здесь он работает. Но не мог понять, почему так?
l1
# => "java"
l2
# => "haskell"
l1,(*),l2 = *langs
# => ["java", "csharp", "ruby", "haskell"]
l1
# => "java"
l2
# => "ruby"
Из приведенного выше примера кажется, что он пропускает элементы массива.
Вопросы:
-
(a) что вызывается оператор (*)?
-
(b) когда я использовал в splat (*) в строке l1,*,l2 = *langs
, он потребляет все элементы - "csharp", "ruby"
. Есть ли способ увидеть, что *
потребляет там технически? Очевидно, что я связываюсь с использованием, если l1,*,l2 = *langs
не l1,l*,l2 = *langs
.
Ответы
Ответ 1
Это связано с как скобки работают с параллельным назначением, как объясняется Matz.
Например:
a, b, c = *[1, 2, 3]
a => 1
b => 2
c => 3
Отличается от:
a, (b, c) = *[1, 2, 3]
a => 1
b => 2
c => nil
В принципе, в скобках говорится: присвойте элементу правой руки этот индекс переменным в parens. Таким образом, 2 присваивается b
, и ничего не осталось в индексе 1
для назначения c
. Аналогично, (*) возьмет только элемент в заданном индексе и распределит его.
# the * is interpreted to mean 'take all remaining elements'
a, * = 1, 2, 3, 4
# the * is interpreted to mean 'take all remaining elements except
# the last element'
a, *, c = 1, 2, 3, 4
# incorrect syntax, can't splat more than once on all remaining
# elements
a, *, *, c = 1, 2, 3, 4
# the * is interpreted to mean 'take all elements at index 1'
a, (*), c = 1, 2, 3, 4
# the * are interpreted to mean 'take all elements at index 1,
# then again at index 2'
a, (*), (*), c = 1, 2, 3, 4
Обычно оператор *
используется при объединении с переменной как *foo
- но если нет, он будет удерживать свое место и принимать назначения элементов, как если бы они были переменной (по существу отбрасывая их)
Ответ 2
Подумайте об этом так: скобки сами по себе (игнорируя имя переменной или оператор splat) обращаются к одному элементу из массива:
l1, (), (), l2 = *['a', 'b', 'c', 'd']
^ ^ ^ ^
'a' 'b' 'c' 'd'
Если бы это был массив массивов, было бы разумнее использовать скобки:
>> l1,(l3, l4),(l5, l6),l2 = *['a', ['b', 'c'], ['d', 'e'], 'f']
=> ["a", ["b", "c"], ["d", "e"], "f"]
>> l1
=> "a"
>> l3
=> "b"
>> l4
=> "c"
>> l5
=> "d"
>> l6
=> "e"
>> l2
=> "f"
Следовательно, (*) принимает один элемент из массива и присваивает ему знак. Скобки сами берут элемент SINGLE, тогда splat принимает этот единственный элемент и "разбивает" его.
Следует отметить, что при выполнении присвоения переменной переменной из массива на стороне массива нет необходимости:
>> a,b,c = ['a', 'b', 'c']
=> ["a", "b", "c"]
>> a
=> "a"
>> b
=> "b"
>> c
=> "c"
Ответ 3
(*)
действительно просто читает один элемент с правой стороны. Рассмотрим этот пример, который имеет пятый элемент в массиве langs
:
langs = ["java", "csharp", "ruby", "haskell", "python" ]
Итак, когда вы используете обычный знак, вы получаете:
l1,*,l2 = langs
l1 #=> "java"
l2 #=> "python"
в отличие от вашего примера с круглыми скобками:
l1,(*),(*),l2 = langs
l1 #=> "java"
l2 #=> "haskell"
Я хочу упомянуть, что для этого случая вы обычно использовали бы _
для присвоения средним значениям "ничего" (что эквивалентно последнему примеру):
l1,_,_,l2 = langs
l1 #=> "java"
l2 #=> "haskell"
Если вы хотите увидеть, что находится внутри средних значений, вы можете явно присвоить значения таким переменным:
l1,*m,l2 = *langs
l1 #=> "java"
l2 #=> "python"
m #=> ["csharp","ruby","haskell"]
или с другим примером:
l1,(*m1),(*m2),l2 = langs
l1 #=> "java"
l2 #=> "haskell"
m1 #=> ["csharp"]
m2 #=> ["ruby"]
Поэтому я надеюсь, что это ясно, что (*)
не является оператором сам по себе, но на самом деле является просто символом в круглых скобках и поэтому не имеет собственного имени.