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.