Это легко объясняется:
Итак:
def x(a=0, b=[], c=[], d=0):
a = a + 1
b = b + [1]
c.append(1)
print a, b, c
a
не изменяется - каждый вызов назначения создает новый объект int - печатается новый объект b
не изменяется - новый массив создается из значения по умолчанию и печатается c
изменения - операция выполняется на одном и том же объекте - и печатается Синтаксис обновления записи входит в стандартную комплектацию с компилятором:
addManStk team = team {
manager = (manager team) {
diet = (diet (manager team)) {
steaks = steaks (diet (manager team)) + 1
}
}
}
Ужасно! Но есть лучший способ. В Hackage есть несколько пакетов, которые реализуют функциональные ссылки и линзы, что определенно то, что вы хотите сделать. Например, с пакетом fclabels вы должны поставить знаки подчеркивания перед всеми вашими именами записей, а затем записать
$(mkLabels ['BBTeam, 'Coach, 'Diet, 'BBPlayer])
addManStk = modify (+1) (steaks . diet . manager)
Отредактировано в 2017 году, чтобы добавить: в наши дни существует широкое консенсус по пакету линзы является особенно хорошей методикой реализации. Хотя это очень большой пакет, есть также очень хорошая документация и вводные материалы, доступные в разных местах в Интернете.
Вот как вы могли бы использовать комбинаторы семантического редактора (SEC), как предложил Lambdageek.
Сначала несколько полезных сокращений:
type Unop a = a -> a
type Lifter p q = Unop p -> Unop q
Здесь Unop
«семантический редактор», а Lifter
- комбинатор семантического редактора. Некоторые лифтеры:
onManager :: Lifter Coach BBTeam
onManager f (BBTeam n m p) = BBTeam n (f m) p
onDiet :: Lifter Diet Coach
onDiet f (Coach n c d) = Coach n c (f d)
onStakes :: Lifter Integer Diet
onStakes f (Diet n s e) = Diet n (f s) e
Теперь просто составите SEC, чтобы сказать, что вы хотите, а именно добавьте 1 к дозам диеты менеджера (команды):
addManagerSteak :: Unop BBTeam
addManagerSteak = (onManager . onDiet . onStakes) (+1)
. Сравнивая с подходом SYB, версия SEC требует дополнительной работы для определения SEC, и я только предоставил те, которые нужны в этом примере. SEC позволяет целевое приложение, которое было бы полезно, если бы у игроков были диеты, но мы не хотели их подстраивать. Возможно, есть хороший способ SYB справиться с этим различием.
Изменить: Вот альтернативный стиль для основных SEC:
onManager :: Lifter Coach BBTeam
onManager f t = t { manager = f (manager t) }
Позже вы также можете взглянуть на некоторые общие библиотеки программирования: когда сложность ваших данных увеличивается, и вы обнаруживаете, что пишете больше и шаблонный код (например, увеличиваете содержимое стейка для игроков, диеты тренеров и пиво наблюдателей), который по-прежнему является шаблоном даже в менее сложной форме. SYB , вероятно, самая известная библиотека (и поставляется с платформой Haskell). На самом деле оригинальная работа по SYB использует очень похожую проблему для демонстрации подхода:
Рассмотрим следующие типы данных, которые описывают организационную структуру компании. Компания разделена на отделы. Каждый отдел имеет менеджера и состоит из набора подразделений, где подразделение является либо единственным сотрудником, либо отделом. Как менеджеры, так и обычные сотрудники - это просто люди, получающие зарплату.
[skiped]
Теперь предположим, что мы хотим увеличить зарплату всех в компании на определенный процент. То есть мы должны написать функцию:
увеличение :: Float -> Компания -> Компания
blockquote>(остальное в документе - рекомендуется чтение)
Конечно, в вашем примере вам просто нужно получить доступ / изменить одну часть крошечной структуры данных, чтобы она не требовала универсального подхода (все же решение на основе SYB для вашей задачи ниже), но как только вы увидите повторение код / шаблон доступа / модификации вы хотите проверить эту или другие общие библиотеки программирования.
{-# LANGUAGE DeriveDataTypeable #-} import Data.Generics data BBTeam = BBTeam { teamname :: String, manager :: Coach, players :: [BBPlayer]} deriving (Show, Data, Typeable) data Coach = Coach { coachname :: String, favcussword :: String, diet :: Diet } deriving (Show, Data, Typeable) data Diet = Diet { dietname :: String, steaks :: Integer, eggs :: Integer} deriving (Show, Data, Typeable) data BBPlayer = BBPlayer { playername :: String, hits :: Integer, era :: Double } deriving (Show, Data, Typeable) incS d@(Diet _ s _) = d { steaks = s+1 } addManagerSteak :: BBTeam -> BBTeam addManagerSteak = everywhere (mkT incS)
manager = (manager team) { diet = (diet (manager team)) { steaks = steaks (diet (manager team)) + 1 }}
– Omari Norman 12 February 2014 в 22:04