Общие сведения о вызове и недостатке метода invix (: :) в Scala

18

Я довольно новичок в языке программирования Scala и пытаюсь что-то затеять у меня в голове, когда я слежу за лекциями в .

Я думаю, что не мог понять, как работает оператор cons, вот некоторые вещи, которые я пробовал:

Я создал генератор псевдослучайных чисел, затем попытался создать список из одного случайного значения:

scala> val gen = new java.util.Random
gen: java.util.Random = [email protected]

scala> gen nextInt 3 :: Nil
<console>:7: error: type mismatch;
 found   : List[Int]
 required: Int
       gen nextInt 3 :: Nil
                     ^

Но он попытался передать List (3) методу nextnt. Когда я использовал паратесы, проблем не было.

scala> (gen nextInt 3) :: Nil
res69: List[Int] = List(1)

Мне было интересно узнать о порядке выполнения, поэтому я создал функцию для проверки

scala> def pr(i:Int):Int = { println(i); i }
pr: (i: Int)Int

scala> pr(1) :: pr(2) :: pr(3) :: Nil
1
2
3
res71: List[Int] = List(1, 2, 3)

Как видно из выходов, порядок выполнения совпадает с порядком появления. Тогда я подумал, что это может быть о функции nextInt, после чего я попытался выполнить следующие действия:

scala> 1 + 2 :: Nil
res72: List[Int] = List(3)

Он сначала выполнил дополнение, и после этого выполнено cons. Итак, вот вопрос: в чем разница между gen nextInt 3 :: Nil и 1 + 2 :: Nil ?

    
задан ciuncan 05.07.2010 в 21:26
источник

2 ответа

40

Здесь есть две вещи: приоритет и неподвижность . Как упоминалось в sepp2k, этот вопрос о переполнении стека объясняет приоритет, считая, что правила, как указано, недостаточно полны, и произошли очень небольшие изменения от Scala 2.7 до Scala 2.8. Однако различия относятся главным образом к операторам, заканчивающимся в = .

Что касается fixity , почти все в Scala читается слева направо, к чему привыкли программисты. Однако в Scala операторы, заканчивающиеся на : , читаются справа налево.

Возьмем, таким образом, этот пример:

1 + 2 :: Nil

Во-первых, приоритет. Что имеет наибольшее преимущество, + или : ? Согласно таблице, + имеет приоритет над : , поэтому добавление выполняется в первую очередь. Следовательно, выражение равно этому:

((1).+(2)) :: Nil

Теперь нет конфликта с приоритетом, но поскольку :: заканчивается на : , он имеет различную фиксацию. Он читается справа налево, поэтому:

Nil.::((1).+(2))

С другой стороны, в этом:

gen nextInt 3 :: Nil

Оператор :: имеет приоритет над nextInt , потому что : имеет приоритет над всеми буквами. Поэтому, помня о его фиксации, он становится:

gen nextInt Nil.::(3)

Что тогда становится

gen.nextInt(Nil.::(3))

В этот момент ошибка очевидна.

PS: Я пишу (1).+(2) вместо 1.+(2) , потому что на момент написания этой статьи 1. интерпретируется как двойное число, делая 1.+(2) выражением infix, добавляя double 1.0 to 2. Этот синтаксис устарел от Scala 2.10.0 и, вероятно, не будет присутствовать на Scala 2.11.

    
ответ дан Daniel C. Sobral 05.07.2010 в 22:58
источник
  • Спасибо за ваш подробный ответ, он дал мне много подсказок о многом другом. Но у меня есть еще один вопрос: как компилятор интерпретирует выражение в третьем разделе кода, который я дал в моем первоначальном вопросе; с точки зрения скобок? потому что использование функции pr показало, что условия выполняются в порядке слева направо. –  ciuncan 06.07.2010 в 03:45
  • Правильно, вызовы функций оцениваются слева направо. Это потому, что pr (X) - это выражения аргументов :: и они сначала оцениваются по порядку, а затем передаются в метод. pr (1) :: pr (2) :: pr (3) :: Nil prints 1 2 3. Но Nil.: :( pr (3)) .: :( pr (2)) .: :( pr ( 1)) печатает 3 2 1. Но оба возвращают List (1, 2, 3) –  michael.kebe 06.07.2010 в 08:31
  • @ciuncan Я не знаю. Возможно, что нотация оператора с правильной фиксацией позволяет оценить порядок оценки с помощью точечной нотации, иначе может быть, что компилятор оптимизирует :: вызовы, но имеет ошибку в отношении порядка оценки. –  Daniel C. Sobral 06.07.2010 в 12:08
  • Спасибо за ответ. Я буду исследовать дальше, и если найду что-нибудь, я отправлю сюда. –  ciuncan 06.07.2010 в 18:50
  • @ DanielC.Sobral В вашем примере вам не должно быть места перед точкой. Если это не так: (1. + (2)) :: Nil В противном случае вы конвертируете 1 в double. –  dublintech 17.01.2013 в 10:50
Показать остальные комментарии
3

Это приоритет, а не порядок выполнения. + имеет более высокий приоритет, чем :: , поэтому a + b :: c анализирует как (a + b) :: c . Однако вызовы метода infix с регулярными именами имеют более низкий приоритет, поэтому a foo b c анализирует как a foo (b c) .

См. этот вопрос для списка операторов, упорядоченных по их приоритету в scala.     

ответ дан sepp2k 05.07.2010 в 21:37
источник
  • Спасибо за ваше объяснение. Приоритетом было то, о чем я просто не подумал. –  ciuncan 06.07.2010 в 03:39