Какие хорошие право-ассоциативные методы в Scala?

Я только начал играть с Scala, и я только что узнал о том, как методы могут быть право-ассоциативными (в отличие от более традиционной лево-ассоциативности, характерной для императивных объектно-ориентированных языков).

Во-первых, когда я увидел пример кода, чтобы cons список в Scala, я заметил, что каждый пример всегда имел Список в правой части:

 println(1 :: List(2, 3, 4)) newList = 42 :: originalList 

Однако, даже после того, как я это видел снова и снова, я не думал об этом дважды, потому что я не знал (в то время), что :: метод в List . Я просто предположил, что это оператор (опять же, в смысле оператора в Java), и что ассоциативность не имеет значения. Тот факт, что List всегда появлялся с правой стороны в примере кода, просто казался случайным (я думал, что это, возможно, просто «предпочтительный стиль»).

Теперь я знаю лучше: он должен быть написан таким образом, потому что :: является право-ассоциативным.

Мой вопрос в том, в чем смысл определять право-ассоциативные методы?

Это чисто по эстетическим соображениям, или же правозависимость действительно имеет какую-то выгоду от лево-ассоциативности в определенных ситуациях?

С моей (новички) точки зрения, я действительно не вижу, как

 1 :: myList 

лучше, чем

 myList :: 1 

но это, очевидно, такой тривиальный пример, что я сомневаюсь, что это справедливое сравнение.

Короткий ответ заключается в том, что право-ассоциативность может улучшить читаемость, сделав то, что тип программиста соответствует тому, что на самом деле делает программа.
Итак, если вы наберете « 1 :: 2 :: 3 », вы получите список (1, 2, 3) назад, а не получите список в совершенно другом порядке.
Это было бы потому, что « 1 :: 2 :: 3 :: Nil » на самом деле

 List[Int].3.prepend(2).prepend(1) scala> 1 :: 2 :: 3:: Nil res0: List[Int] = List(1, 2, 3) 

который является одновременно:

  • более читаемый
  • более эффективный (O (1) для prepend , против O (n) для гипотетического метода append )

(Напоминание, выдержка из книги « Программирование в Скале» )
Если метод используется в обозначении оператора, например, a * b , метод вызывается в левом операнде, как в a.*(b) – если имя метода не заканчивается в двоеточие.
Если имя метода заканчивается в двоеточии, метод вызывается в правом операнде.
Следовательно, в 1 :: twoThree метод :: вызывается на twoThree , проходя в 1, например: twoThree.::(1) .

Для списка он играет роль операции добавления (список, по-видимому, добавляется после «1», чтобы сформировать « 1 2 3 », где на самом деле это 1, который добавлен в список).
Список classов не предлагает действительную операцию добавления, поскольку время, которое требуется для добавления в список, растет линейно с размером списка, тогда как добавление с :: принимает постоянное время .
myList :: 1 попытался бы довести весь контент myList до ‘1’, который будет длиннее, чем добавление 1 в myList (как в ‘ 1 :: myList ‘).

Примечание. Независимо от того, какая ассоциативность имеет оператор, его операнды всегда оцениваются слева направо.
Поэтому, если b является выражением, которое является не просто ссылкой на неизменяемое значение, то a ::: b более точно рассматривается как следующий блок:

 { val x = a; b.:::(x) } 

В этом блоке a все еще оценивается до b, а затем результат этой оценки передается в качестве операнда для метода b’s :::.


зачем вообще различать лево-ассоциативные и право-ассоциативные методы?

Это позволяет сохранить обычную 1 :: myList операцию (‘ 1 :: myList ‘), фактически применяя операцию в правильном выражении, потому что;

  • он более эффективен.
  • но это более читаемо с помощью обратного ассоциативного порядка (‘ 1 :: myList ‘ против ‘ myList.prepend(1) ‘)

Как вы говорите, «синтаксический сахар», насколько я знаю.
Обратите внимание, что в случае foldLeft , например, они, возможно, немного ушли далеко (с эквивалентным эквивалентным оператором « /: »)


Чтобы включить некоторые из ваших комментариев, слегка перефразировали:

если вы считаете функцию «добавить» лево-ассоциативной, тогда вы должны написать « oneTwo append 3 append 4 append 5 ».
Однако, если бы это было добавление 3, 4 и 5 к одному (что вы бы предположили, кстати, что оно написано), это будет O (N).
То же самое с «::», если это было для «append». Но это не так. Это на самом деле для “preend”

Это означает, что « a :: b :: Nil » относится к « List[].b.prepend(a) »

Если «::» должны были добавляться и оставаться лево-ассоциативными, тогда результирующий список был бы в неправильном порядке.
Вы ожидали бы, что он вернет List (1, 2, 3, 4, 5), но это приведет к возврату списка (5, 4, 3, 1, 2), что может быть неожиданным для программиста.
Это потому, что вы сделали бы в лево-ассоциативном порядке:

 (1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2) 

Таким образом, правая ассоциативность делает код совпадающим с фактическим порядком возвращаемого значения.

В чем смысл определения право-ассоциативных методов?

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

Перегрузка оператора – полезная вещь, поэтому Скала сказала: «Почему бы не открыть ее до любой комбинации символов? Скорее, зачем делать различия между операторами и методами? Теперь, как разработчик библиотеки взаимодействует со встроенными типами, такими как Int ? В C ++ она использовала бы функцию friend в глобальной области. Что делать, если мы хотим, чтобы все типы выполняли оператор :: ?

Правильная ассоциативность дает чистый способ добавить :: operator ко всем типам. Конечно, технически оператор :: – это метод типа List. Но это также убедительный оператор для встроенного Int и всех других типов, по крайней мере, если вы можете игнорировать :: Nil в конце.

Я думаю, что это отражает философию Scala в реализации как можно большего количества функций в библиотеке и делает язык гибким для их поддержки. Это позволяет кому-то придумать SuperList, который можно назвать:

 1 :: 2 :: SuperNil 

Несколько печально, что правая ассоциативность в настоящее время жестко закодирована только до двоеточия в конце, но я думаю, это позволяет легко запомнить.

Правильная ассоциативность и левоассоциативность играют важную роль при выполнении операций свертки списков. Например:

 def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldRight ys)(_::_) 

Это прекрасно работает. Но вы не можете выполнить то же самое с помощью операции foldLeft

 def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldLeft ys)(_::_) 

, потому что :: право-ассоциативный.

  • Что делает знак вопроса и точечный оператор?. означает в C # 6.0?
  • Оператор копирования и оператор присваивания
  • Что означает $ mean / do в Haskell?
  • Является ли использование && короткого замыкания безопасным в .NET?
  • Каковы операторы Pointer-to-Member -> * и. * В C ++?
  • Сделать оператор << виртуальным?
  • Есть ли условный тернарный оператор в VB.NET?
  • Можно ли получить указатель на функцию встроенного стандартного оператора?
  • Вопросительный знак и двоеточие в JavaScript
  • Разница между ++ Var и Var ++
  • Что означает «| =»? (оператор равных труб)
  • Interesting Posts

    Анализ CSV в java

    Удалить USB-устройство из командной строки

    ОШИБКА: разрешение отклонено для отношения tablename на Postgres при попытке SELECT как пользователя, использующего только для чтения

    андроидная анимация не завершена inAnimationEnd

    JasperException: значение атрибута classа useBean недопустимо.

    Должны ли «использовать» директивы внутри или вне пространства имен?

    Расширитель компонентов с декоратором базового classа

    Получение атрибутов значения Enum

    Почему Eclipse Android Device Chooser не показывает мое устройство Android?

    Объединить DLL в EXE?

    Как я могу перечислить все IP-адреса в подключенной сети, предпочтительно с помощью терминала?

    CSS rotation кросс-браузер с jquery.animate ()

    Средство просмотра огромных изображений под Linux (> 100 МП цветных изображений)

    Как получить подписчиков мероприятия?

    Как вы завершаете или перезагружаете компьютер Windows через подключение к удаленному рабочему столу?

    Давайте будем гением компьютера.