Переглядаючи прелюдію Хаскелла, я бачу функцію 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