Scala: var List vs val MutableList

В книге Odersky et al. Scala они говорят, что используют списки. Я не читал обложку книги, но все примеры, похоже, используют val List. Насколько я понимаю, также рекомендуется использовать валы над варами. Но в большинстве приложений не существует компромисса между использованием var List или val MutableList?. Очевидно, мы используем список Val, когда можем. Но хорошо ли использовать множество списков var (или var Vectors и т.д.)?

Я новичок в Scala, начиная с С#. Там у меня было много:

public List<T> myList {get; private set;}

которые могут быть легко объявлены как vals, если С# имеет неизменяемость, потому что сама коллекция никогда не изменялась после построения, хотя элементы были добавлены и вычтены из коллекции в течение ее жизненного цикла. Поэтому объявление коллекции var почти похоже на шаг от неизменности.

В ответ на ответы и комментарии одна из сильных точек продажи Scala заключается в том, что она может иметь много преимуществ без необходимости полностью изменять способ написания кода, как это имеет место с помощью слова Lisp или Haskell.

Ответы

Ответ 1

Хорошо ли использовать множество списков var (или var Vectors и т.д.)?

Я бы сказал, что лучше использовать var с неизменяемыми коллекциями, чем использовать val с изменчивыми. Сверху моей головы, потому что

  • У вас больше гарантий относительно поведения: если ваш объект имеет изменяемый список, вы никогда не знаете, будет ли какой-либо другой внешний объект обновлять его.

  • Вы ограничиваете степень изменчивости; методы, возвращающие коллекцию, принесут неизменный, поэтому у вас есть только mutablility внутри вашего одного объекта.

  • Легко обезвреживать var, просто присваивая его val, тогда как для того, чтобы сделать изменяемую коллекцию неизменной, вам нужно использовать другой тип коллекции и перестроить ее

В некоторых случаях, таких как зависящие от времени приложения с обширным вводом-выводом, самым простым решением является использование изменчивого состояния. И в некоторых обстоятельствах изменчивое решение просто более элегантно. Однако в большинстве кодеков вам вообще не нужна изменчивость. Ключ состоит в том, чтобы использовать коллекции с функциями более высокого порядка вместо циклов или рекурсии, если подходящая функция не существует. Это проще, чем кажется. Вам просто нужно потратить некоторое время на изучение методов в List (и других коллекциях, которые в основном одинаковы). Самые важные из них:

map: применяет вашу предоставленную функцию к каждому элементу коллекции - вместо использования циклов и обновления значений в массиве

foldLeft: возвращает один результат из коллекции - вместо циклов и обновления переменной аккумулятора

for-yield выражения: упростить сопоставление и фильтрацию, особенно для проблем типа вложенного цикла

В конечном счете, большая часть функционального программирования является следствием неизменности, и вы не можете иметь ее без другого; однако локальные вары в основном являются деталями реализации: нет ничего плохого в некоторой изменчивости, если она не может выйти из локальной области. Поэтому используйте vars с неизменяемыми коллекциями, поскольку локальные vars не будут экспортироваться.

Ответ 2

Вы предполагаете, что параметр List должен быть изменен или что-то, что указывает на него, должно быть изменчивым. Другими словами, вам нужно выбрать один из следующих вариантов:

val list: collection.mutable.LinkedList[T]
var list: List[T]

Это ложная дихотомия. Вы можете иметь оба:

val list: List[T]

Итак, вопрос, который вы должны задавать, - это как это сделать?, но мы можем ответить только тогда, когда вы попробуете его и столкнетесь с конкретной проблемой. Нет никакого общего ответа, который разрешит все ваши проблемы (ну, есть - монады и рекурсия - но это слишком общее).

Итак... попробуй. Вам может быть интересно посмотреть Обзор кода, где большинство вопросов Scala относятся к тому, как сделать код более функциональным. Но, в конечном счете, я думаю, что лучший способ сделать это - попробовать и прибегнуть к Stack Overflow, когда вы просто не можете понять какую-то конкретную проблему.

Ответ 3

Вот как я вижу эту проблему изменчивости в функциональном программировании.

Наилучшее решение: Значения лучше всего, поэтому наилучшим в использовании функционального программирования являются значения и рекурсивные функции:

val myList = func(4); 
def func(n) = if (n>0) n::func(n) else Nil

Нужна изменчивость: Иногда требуется изменить материал или сделать все намного проще. Мое впечатление, когда мы сталкиваемся с этой ситуацией, заключается в использовании структур mutables, поэтому для использования val list: collection.mutable.LinkedList[T] вместо var list: List[T] это происходит не из-за реального улучшения производительности, а из-за изменчивых функций, которые уже определены в изменяемой коллекции.

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

Ответ 4

Я считаю, что вы не можете отделить вопрос об изменяемом val/неизменяемом var от конкретного варианта использования. Позвольте мне углубиться: есть два вопроса, которые вы хотите задать себе:

  • Как я могу разоблачить мою коллекцию снаружи?
    • Мне нужен снимок текущего состояния, и этот снимок не должен меняться независимо от изменений, внесенных в объект, принимающий коллекцию. В таком случае вы должны предпочесть неизменный var. Причина в том, что единственный способ сделать это с помощью изменяемого val - через защитную копию.
    • Я хочу просмотреть состояние, которое должно измениться, чтобы отразить изменения в исходном состоянии объекта. В этом случае вы должны выбрать неизменяемый val и вернуть его через немодифицируемую оболочку (как это делает Collections.unmodifiableList() на Java, здесь является вопрос, где он спросил, как это сделать в Scala). Я не вижу возможности достичь этого с неизменным var, поэтому я считаю, что ваш выбор здесь является обязательным.
    • Меня беспокоит отсутствие побочных эффектов. В этом случае оба подхода очень похожи. С неизменяемым var вы можете напрямую вернуть внутреннее представление, поэтому оно может быть немного понятным.
  • Как мне изменить мою коллекцию?
    • Обычно я делаю массовые изменения, а именно, я устанавливаю всю коллекцию сразу. Это делает неизменным var лучший выбор: вы можете просто назначить то, что вводите в текущее состояние, и вы уже в порядке. С неизменным val вам нужно сначала очистить свою коллекцию, а затем скопировать новое содержимое. Определенно хуже.
    • Я обычно делаю поточечные изменения, а именно, добавляю/удаляю один элемент (или несколько из них) в/из коллекции. Это то, что я на самом деле вижу большую часть времени, причем коллекция представляет собой только деталь реализации и trait только методы раскрытия для точечного манипулирования ее статусом. В этом случае изменяемый val может быть, как правило, более эффективным по производительности, но в случае, если это проблема, я рекомендую взглянуть на Scala производительность коллекций.

Ответ 5

Если нужно использовать var-списки, почему бы и нет? Чтобы избежать проблем, вы можете, например, ограничить область действия переменной. Некоторое время назад был аналогичный вопрос с довольно хорошим ответом: scala изменяемый и неизменный набор, когда нужно использовать val и var.