Переглядаючи прелюдію Хаскелла, я бачу функцію const
:
const x _ = x
Здається, я не можу знайти нічого відповідного щодо цієї функції.
В чому справа? Хтось може навести приклад, де ця функція може використовуватися?
Переглядаючи прелюдію Хаскелла, я бачу функцію 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")
Додамо до чудової прямої відповіді Hammar: скромні функції подобаються 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
може бути просто реалізацією, яку ви шукаєте разом із іншими функціями. Ось приклад, який я виявив.
Скажімо, ми хочемо переписати структуру 2-кортежів на іншу структуру 2-кортежів. Я міг би висловити це так:
((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 .
Коли ви починаєте абстрактно, до мети, а потім працюєте над реалізацією, ви можете бути здивовані відповідями. Тобто будь-яка одна функція може бути такою ж загадковою, як будь-яка гвинтик машини. Якщо ви потягнетеся назад, щоб побачити всю машину, ви зможете зрозуміти контекст, в якому цей гвинтик необхідний.
Скажімо, ви хочете створити список, Nothings
рівний довжині рядка. Як const
повертає свій перший аргумент, незалежно від другого, ви можете зробити:
listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing
або, більш чітко:
listOfNothing st = map (const Nothing) st
Скажімо, ви хочете повернути список. Це ідіоматичний спосіб зробити це в 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