Haskell: цель функции переворота?

Я немного удивлен, что об этом не спросили раньше. Может это глупый вопрос.

Я знаю, что флип меняет порядок двух аргументов.

Пример:

(-) 5 3
= 5 - 3
= 2

flip (-) 5 3
= 3 - 5
= -2

Но зачем мне такая функция? Почему бы просто не изменить ввод вручную?

Почему бы просто не написать:

(-) 3 5
= 3 - 5
= -2
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
1 786
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Маловероятно, что когда-либо будет использоваться функция flip для функции, которая сразу применяется к двум или более аргументам, но flip может быть полезен в двух ситуациях:

  1. Если функция передается более высокого порядка другой функции, нельзя просто изменить аргументы в месте вызова, поскольку сайт вызова находится в другой функции! Например, эти два выражения дают очень разные результаты:

    ghci> foldl (-) 0 [1, 2, 3, 4]
    -10
    ghci> foldl (flip (-)) 0 [1, 2, 3, 4]
    2
    

    В этом случае мы не можем поменять местами аргументы (-), потому что мы не применяем (-) напрямую; foldl применяет это для нас. Таким образом, мы можем использовать flip (-) вместо записи всей лямбда-выражения \x y -> y - x.

  2. Кроме того, может быть полезно использовать flip для частичного применения функции ко второму аргументу. Например, мы могли бы использовать flip для написания функции, которая строит бесконечный список, используя функцию построителя, которой предоставляется индекс элемента в списке:

    buildList :: (Integer -> a) -> [a]
    buildList = flip map [0..]
    
    ghci> take 10 (buildList (\x -> x * x))
    [0,1,4,9,16,25,36,49,64,81]
    

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

    ghci> map (flip map [1, 2, 3]) [(+ 1), (* 2)]
    [[2,3,4],[2,4,6]]
    

    Иногда вместо использования flip в таком случае люди будут использовать инфиксный синтаксис, поскольку операторские секции имеет уникальное свойство, которое они могут предоставить первым вторым аргументом или же функции. Следовательно, запись (`f` x) эквивалентна записи flip f x. Лично я считаю, что писать flip напрямую обычно легче, но это дело вкуса.

В вашем примере buildList вы просто указываете функцию buildList, например buildList f = map f [0 ..]

simplesystems 01.07.2018 08:49

@simplesystems Да, вы всегда можете написать то же самое без flip, просто вставив определение flip на сайте его использования. Но использование flip позволяет писать в безточечный стиль, который некоторые люди считают более читаемым, а некоторые - нет. Лично я считаю, что это ситуационный характер - я думаю, что иногда бесточечный стиль устраняет шум и упрощает понимание, - но в этом случае, я согласен, версия с явным аргументом действительно не хуже.

Alexis King 01.07.2018 08:52

Другими словами, вы всегда можете заменить flip f на \x y -> f y x, так что flip ни в коем случае не обязателен! Но, как и все функции, это абстракция, хоть и небольшая.

Alexis King 01.07.2018 08:53

@simplesystems Возможно, вас заинтересует stackoverflow.com/q/7402528/176841. Есть некоторая теория, которую вы можете изучить здесь, если хотите: в haskell flip позволяет нам избегать использования лямбда-выражений (для ясности, эстетики); Интересно, что лямбды никогда не являются нужно: можно выбрать несколько функций («комбинаторов», таких как flip), так что у вас есть такая мощная система, как лямбда-исчисление. Это называется основа, наиболее известным из которых является комбинаторное исчисление SKI.

jberryman 01.07.2018 20:21
flip обычно называется C и также может составлять часть основы: en.wikipedia.org/wiki/B,_C,_K,_W_system. Я пытаюсь понять, что flipconst, и id) не произвольный, но на самом деле вы обнаружите это в теории, если вы думаете о том, как вы могли бы выразить вычисления, не прибегая к лямбдам (что часто является целью в haskell для ясности / эстетических соображений, как описано в других ответах здесь)
jberryman 01.07.2018 20:24

Иногда вы захотите использовать функцию, указав второй параметр, но взяв первый параметр откуда-то еще. Например:

map (flip (-) 5) [1..5]

Хотя это также можно записать как:

map (\x -> x - 5) [1..5]

Другой вариант использования - когда второй аргумент длинный:

flip (-) 5 $
   if odd x
      then x + 1
      else x

Но вы всегда можете использовать выражение let, чтобы назвать первое вычисление параметра, а затем не использовать flip.

что означает знак $ в третьем примере?

simplesystems 01.07.2018 08:51

+1, особенно мне нравится твой последний пример. Я определенно видел это время от времени (и, возможно, я даже написал это сам один или два раза), и я не думал включать это в свой ответ. :)

Alexis King 01.07.2018 08:54

simplesystems, я использую его вместо скобок. Подробнее об этом можно прочитать здесь: haskell-lang.org/tutorial/operators

soupi 01.07.2018 09:27

Алексис, да, я обнаружил, что этот вариант использования мне больше подходит! (Во многих случаях я также предпочитаю использовать обратные кавычки вместо переворота).

soupi 01.07.2018 09:30

Один очень полезный пример использования flip - сортировка по убыванию. Вы можете увидеть, как это работает в ghci:

ghci> import Data.List

ghci> :t sortBy 
sortBy :: (a -> a -> Ordering) -> [a] -> [a]

ghci> :t compare
compare :: Ord a => a -> a -> Ordering

ghci> sortBy compare [2,1,3]
[1,2,3]

ghci> sortBy (flip compare) [2,1,3]
[3,2,1]

Другие вопросы по теме