Просматривая Haskell Prelude, я вижу функцию const
:
const x _ = x
Я не могу найти ничего подходящего относительно этой функции.
В чем смысл? Кто-нибудь может привести пример, где можно использовать эту функцию?
Просматривая Haskell Prelude, я вижу функцию const
:
const x _ = x
Я не могу найти ничего подходящего относительно этой функции.
В чем смысл? Кто-нибудь может привести пример, где можно использовать эту функцию?
Ответы:
Это полезно для перехода к функциям более высокого порядка, когда вам не нужна их гибкость. Например, оператор монадической последовательности >>
может быть определен в терминах оператора монадического связывания как
x >> y = x >>= const y
Это немного удобнее, чем использование лямбды
x >> y = x >>= \_ -> y
и вы даже можете использовать его бесплатно
(>>) = (. const) . (>>=)
хотя я не особо рекомендую это в данном случае.
map (const 42) [1..5]
получается [42, 42, 42, 42, 42]
.
const
полезен для применения к одному аргументу, чтобы получить функцию там, где он нужен (например, переход к map
).
head = foldr const (error "Prelude.head: empty list")
Чтобы добавить к отличному прямому ответу Хаммара: скромные функции, такие как const
и id
, действительно полезны как функции высшего порядка по той же причине, по которой они являются фундаментальными в исчислении комбинатора SKI .
Не то чтобы я думаю, что вступительные функции haskell были сознательно смоделированы после этой формальной системы или чего-то еще. Просто создать богатые абстракции в haskell очень просто, поэтому вы часто видите, что подобные теоретические вещи оказываются практически полезными.
Shameless плагин, но я писал о том , как Аппликативный экземпляр для (->)
фактически являются S
и K
комбинаторы здесь , если это такая вещь , вы в.
((->) e)
это также монада читателя - с Reader
и тому подобное, просто являющиеся newtype
оболочками - и ask
функция тогда id
, так что это тоже I
комбинатор. Если вы посмотрите на оригинальный вместо BCKW основе Хаскелла Карри, B
, K
и W
являются fmap
, return
и join
соответственно.
Простой пример использования const
- Data.Functor.(<$)
. С помощью этой функции вы можете сказать: у меня есть функтор с чем-то скучным, но вместо этого я хочу иметь в нем еще одну интересную вещь, не меняя форму функтора. Например
import Data.Functor
42 <$ Just "boring"
--> Just 42
42 <$ Nothing
--> Nothing
"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]
Определение таково:
(<$) :: a -> f b -> f a
(<$) = fmap . const
или написано не как бессмысленное:
cool <$ uncool = fmap (const cool) uncool
Вы видите, как const
здесь используется, чтобы «забыть» о вводе.
Я не могу найти ничего подходящего относительно этой функции.
Во многих других ответах обсуждаются относительно эзотерические (по крайней мере, для новичков) приложения const
. Вот простой: вы можете использовать, const
чтобы избавиться от лямбды, которая принимает два аргумента, отбрасывает первый, но делает что-то интересное со вторым.
Например, следующая (неэффективная, но поучительная) реализация length
,
length' = foldr (\_ acc -> 1 + acc) 0
можно переписать как
length' = foldr (const (1+)) 0
что, возможно, более элегантно.
Выражение const (1+)
действительно семантически эквивалентно \_ acc -> 1 + acc
, потому что оно принимает один аргумент, отбрасывает его и возвращает раздел (1+)
.
Другое использование - реализация функций-членов класса, которые имеют фиктивный аргумент, который не должен оцениваться (используется для разрешения неоднозначных типов). Пример, который может быть в Data.bits:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
Используя const, мы явно говорим, что определяем постоянные значения.
Лично мне не нравится использование фиктивных параметров, но если они используются в классе, это довольно хороший способ написания экземпляров.
const
может быть просто реализацией, которую вы ищете в сочетании с другими функциями. Вот пример, который я обнаружил.
Скажем, мы хотим переписать структуру из двух кортежей в другую структуру из двух кортежей. Я могу выразить это так:
((a,b),(c,d)) ⇒ (a,(c,(5,a)))
Я могу дать прямое определение с сопоставлением с образцом:
f ((a,b),(c,d)) = (a,(c,(5,a)))
Что, если мне нужно бессмысленное (неявное) решение для такого рода переписываний? Подумав немного позже, ответ в том, что мы можем выразить любые переписывания с помощью (&&&), const, (.), fst, snd
. Обратите внимание, что(&&&)
это из Control.Arrow
.
Решение примера с использованием этих функций:
(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))
Обратите внимание на сходство с (a,(c,(5,a)))
. Что если заменить &&&
на ,
? Затем он гласит:
(fst.fst, (fst.snd, (const 5, fst.fst)))
Обратите внимание на то, как a
выглядит первый элемент первого элемента, и это то, что fst.fst
проецируется. Обратите внимание, как c
выглядит первый элемент второго элемента, и это то, чтоfst.snd
проецируется. То есть переменные становятся путем к своему источнику.
const
позволяет нам вводить константы. Интересно, как название сочетается со смыслом!
Затем я обобщил эту идею с Applicative , так что вы можете написать какую - либо функцию в бессмысленном стиле (до тех пор , пока у вас есть случай анализ доступен как функции, такие как maybe
, either
, bool
). Опять же, const
играет роль введения констант. Вы можете увидеть эту работу в пакете Data.Function.Tacit .
Когда вы начинаете абстрактно с цели, а затем работаете над реализацией, вы можете быть удивлены ответами. Другими словами, любая функция может быть такой же загадочной, как и любой винтик в машине. Однако, если вы отодвинетесь, чтобы увидеть всю машину, вы сможете понять контекст, в котором этот винтик необходим.
Допустим, вы хотите повернуть список. Это идиоматический способ сделать это в Haskell:
rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs
Эта функция объединяет два массива с функцией const
, первый из которых представляет собой бесконечный циклический массив, а второй - это массив, с которого вы начали.
const
действует как проверка границ и использует исходный массив для завершения циклического массива.
См .: Поворот списка в Haskell
Я не могу найти ничего подходящего относительно этой функции.
Предположим, вы хотите сгенерировать все подпоследовательности данного списка.
Для каждого элемента списка в определенный момент у вас есть выбор: True (включить его в текущую подпоследовательность) или False (не включать). Это можно сделать с помощью функции filterM .
Как это:
λ> import Control.Monad
λ> :t filterM
filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
λ>
Например, нам нужны все подпоследовательности [1..4]
.
λ> filterM (const [True, False]) [1..4]
[[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
λ>
backgroundColor :: Text -> Color
это для меняbackgroundColor = const White