haskell извлекает значение из конструктора данных [дубликат]

Это легко объясняется:

  1. Объявление функции (класс и т. д.) выполняется только один раз, создавая все объекты значения по умолчанию
  2. , все передается по ссылке

Итак:

def x(a=0, b=[], c=[], d=0):
    a = a + 1
    b = b + [1]
    c.append(1)
    print a, b, c
  1. a не изменяется - каждый вызов назначения создает новый объект int - печатается новый объект
  2. b не изменяется - новый массив создается из значения по умолчанию и печатается
  3. c изменения - операция выполняется на одном и том же объекте - и печатается
40
задан amindfv 24 December 2011 в 09:47
поделиться

3 ответа

Синтаксис обновления записи входит в стандартную комплектацию с компилятором:

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 году, чтобы добавить: в наши дни существует широкое консенсус по пакету линзы является особенно хорошей методикой реализации. Хотя это очень большой пакет, есть также очень хорошая документация и вводные материалы, доступные в разных местах в Интернете.

34
ответ дан Daniel Wagner 21 August 2018 в 03:57
поделиться
  • 1
    Благодаря! Только то, что я искал! Не могли бы вы подробнее рассказать о линзах? Это функциональная концепция, которую я понимаю? – Matt Fenwick 9 September 2011 в 19:10
  • 2
    @Matt Fenwick: Больше нечего объяснять о концепции; игнорируя проблемы реализации и производительность, они в значительной степени делают то, что вы хотели сделать. Простейшая реализация - это всего лишь пара функций; один, который извлекает часть из чего-то большего, и тот, который заменяет один и тот же кусок. Единственный трюк заключается в том, что вы можете создавать пары таких функций, создавая пару составных функций, которые могут быть функциональной концепцией, которую вы не видели. – C. A. McCann 9 September 2011 в 19:24
  • 3
    @Matt Fenwick Перегрузка функциональных ссылок - одна из моих лучших 10 любимых пьес Haskell. – Daniel Wagner 9 September 2011 в 19:29
  • 4
    Пример синтаксиса записи неверен и не будет компилироваться. Поскольку обновление записи связывается более жестко, чем функциональное приложение, вам нужно manager = (manager team) { diet = (diet (manager team)) { steaks = steaks (diet (manager team)) + 1 }} – Omari Norman 12 February 2014 в 22:04
  • 5
    @OmariNorman Вы абсолютно правы, спасибо, что указали это. Я скоро отредактирую! – Daniel Wagner 12 February 2014 в 22:08

Вот как вы могли бы использовать комбинаторы семантического редактора (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) }
8
ответ дан Conal 21 August 2018 в 03:57
поделиться

Позже вы также можете взглянуть на некоторые общие библиотеки программирования: когда сложность ваших данных увеличивается, и вы обнаруживаете, что пишете больше и шаблонный код (например, увеличиваете содержимое стейка для игроков, диеты тренеров и пиво наблюдателей), который по-прежнему является шаблоном даже в менее сложной форме. SYB , вероятно, самая известная библиотека (и поставляется с платформой Haskell). На самом деле оригинальная работа по SYB использует очень похожую проблему для демонстрации подхода:

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

[skiped]

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

увеличение :: Float -> Компания -> Компания

(остальное в документе - рекомендуется чтение)

Конечно, в вашем примере вам просто нужно получить доступ / изменить одну часть крошечной структуры данных, чтобы она не требовала универсального подхода (все же решение на основе 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)
5
ответ дан Ed'ka 21 August 2018 в 03:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: