Я всегда заинтересован в изучении новых языков, что держит меня на ногах и делает меня (я считаю) лучшим программистом. Мои попытки захватить Хаскелла приходят и уходят - дважды до сих пор - и я решил, что пришло время попробовать еще раз. В третий раз это очарование, верно?
Неа. Я перечитываю свои старые заметки... и разочаровываюсь: - (
Проблема, которая заставила меня потерять веру в прошлый раз, была простой: перестановки целых чисел. т.е. из списка целых чисел, в список списков - список их перестановок:
[int] -> [[int]]
Это на самом деле общая проблема, поэтому замена "int" выше на "a" все равно будет применяться.
Из моих заметок:
Я сначала его определяю самостоятельно, мне это удается. Ура!
Я посылаю свое решение своему хорошему другу - гуру Хаскелла, он обычно помогает учиться у гуру - и он посылает мне это, о чем я говорю, "выражает истинную силу языка, использование общих средств для кодирования ваших потребностей". Все для этого, я недавно выпил kool-помощь, отпустил:
permute :: [a] -> [[a]]
permute = foldr (concatMap.ins) [[]]
where ins x [] = [[x]]
ins x (y:ys) = (x:y:ys):[ y:res | res <- ins x ys]
Хм. Пусть это сломается:
bash$ cat b.hs
ins x [] = [[x]]
ins x (y:ys) = (x:y:ys):[ y:res | res <- ins x ys]
bash$ ghci
Prelude> :load b.hs
[1 of 1] Compiling Main ( b.hs, interpreted )
Ok, modules loaded: Main.
*Main> ins 1 [2,3]
[[1,2,3],[2,1,3],[2,3,1]]
Хорошо, до сих пор, так хорошо. Потребовал мне минуту, чтобы понять вторую строчку "ins", но ОК: Он помещает 1-й аргумент во все возможные позиции в списке. Круто.
Теперь, чтобы понять foldr и concatMap. в "реальном мире Haskell", объяснили DOT...
(f . g) x
... как еще один синтаксис для...
f (g x)
И в коде, отправленном гуру, DOT использовался из foldr, а функция "ins" как сгиб "сворачивается":
*Main> let g=concatMap . ins
*Main> g 1 [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]
ОК, так как я хочу понять, как DOT используется гуру, я пробую эквивалентное выражение в соответствии с определением DOT (f. g) x = f (g x)...
*Main> concatMap (ins 1 [[2,3]])
<interactive>:1:11:
Couldn't match expected type `a -> [b]'
against inferred type `[[[t]]]'
In the first argument of `concatMap', namely `(ins 1 [[2, 3]])'
In the expression: concatMap (ins 1 [[2, 3]])
In the definition of `it': it = concatMap (ins 1 [[2, 3]])
Что!?! Зачем? Хорошо, я проверяю подпись concatMap и обнаруживаю, что ей нужна лямбда и список, но это просто человеческое мышление; как GHC справляется? Согласно определению DOT выше...
(f.g)x = f(g x),
... то, что я сделал, было действительным, заменяющим:
(concatMap . ins) x y = concatMap (ins x y)
Царапиновая головка...
*Main> concatMap (ins 1) [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]
Итак... Объяснение DOT было очевидно слишком упрощен... DOT должен быть как-то достаточно умен, чтобы понять что мы на самом деле хотели "вставить", чтобы уйти и "съесть" первую аргумент - таким образом, становится функцией, которая хочет работать только на [t] (и "пересекают" их с "1" на всех возможных позициях).
Но где это было указано? Как GHC знал об этом, когда мы вызывали:
*Main> (concatMap . ins) 1 [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]
Была ли подпись "ins" каким-то образом передана эта... политика "есть мой первый аргумент"?
*Main> :info ins
ins :: t -> [t] -> [[t]] -- Defined at b.hs:1:0-2
Я не вижу ничего особенного - "ins" - это функция, которая принимает "t", список 't', и приступает к созданию списка со всеми "interpersals". Ничего о том, чтобы "съесть свой первый аргумент и выкормить его".
Так что... Я озадачен. Я понимаю (через час взглянуть на код!), Что происходит, но... Бог всемогущий... Возможно, GHC пытается понять, сколько аргументов он может "очистить"?
let try with no argument "curried" into "ins",
oh gosh, boom,
let try with one argument "curried" into "ins",
yep, works,
that must be it, proceed)
Снова - yikes...
И поскольку я всегда сравниваю языки, которые я изучаю, с тем, что я уже знаю, как бы "вставить" в Python?
a=[2,3]
print [a[:x]+[1]+a[x:] for x in xrange(len(a)+1)]
[[1, 2, 3], [2, 1, 3], [2, 3, 1]]
Будьте честны, теперь... что проще?
Я имею в виду, я знаю, что я новичок в Haskell, но я чувствую себя идиотом... Глядя на 4 строки кода в течение часа и заканчивая тем, что компилятор... пытается интерпретировать все до тех пор, пока не найдет что-то, что "нажимает"?
Процитировать от смертельного оружия: "Я слишком стар для этого"...