Как я могу использовать fix, и как это работает?
Я был немного смущен документация fix (хотя я думаю, я понимаю, что он должен делать сейчас), поэтому я посмотрел на исходный код. Это еще больше сбило меня с толку:
fix :: (a -> a) -> a
fix f = let x = f x in x
как именно это возвращает неподвижную точку?
я решил попробовать его в командной строке:
Prelude Data.Function> fix id
...
и он висит там. Теперь, чтобы быть справедливым, это на моем старом macbook, который немного медленный. Однако, эта функция не может быть слишком вычислительно дорого, так как все, что передается в id, дает то же самое (не говоря уже о том, что он не съедает процессорного времени). Что я делаю не так?
5 ответов:
вы не делаете ничего плохого.
fix idбесконечный цикл.когда мы говорим, что
fixвозвращает наименьшую фиксированную точку функции, мы имеем в виду, что в теория домен смысле. Так чтоfix (\x -> 2*x-1)и не вернутся1, потому что1является фиксированной точкой этой функции, это не по крайней мере один в заказе домена.я не могу описать порядок доменов в одном абзаце или двух, поэтому Я буду отсылать вас к ссылке теории домена выше. Это отличный учебник, легко читаемый и довольно поучительный. Я очень рекомендую его.
для вида с 10 000 футов,
fix- это функция более высокого порядка, которая кодирует идею рекурсия. Если у вас есть выражение:let x = 1:x in xчто приводит к бесконечному списку
[1,1..], вы могли бы сказать то же самое, используяfix:fix (\x -> 1:x)(или просто
fix (1:)), в котором говорится, найти мне фиксированная точка(1:)функция, IOW значениеxтакое, чтоx = 1:x... как мы определили выше. Как видно из определения,fixне более чем эта идея -- рекурсия инкапсулирована в функцию.это действительно общее понятие рекурсии, а также-вы можете написать любую рекурсивную функцию таким образом, включая функции, которые используют полиморфную рекурсию. Так, например, типичный Фибоначчи функция:
fib n = if n < 2 then n else fib (n-1) + fib (n-2)может быть написано с помощью
fixтаким образом:fib = fix (\f -> \n -> if n < 2 then n else f (n-1) + f (n-2))упражнение: раскройте определение
fixчтобы показать, что эти два определенияfibэквивалентны.но для полного понимания, прочитайте о теории домена. Это действительно классная штука.
я не утверждаю, что понимаю это вообще, но если это кому-то поможет...потом ура.
рассмотрим определение
fix.fix f = let x = f x in x. Умопомрачительная часть заключается в том, чтоxопределяется какf x. Но подумайте об этом на минутку.x = f xтак как x = f x, то мы можем подставить значение
xна правой стороне, верно? Поэтому...x = f . f $ x -- or x = f (f x) x = f . f . f $ x -- or x = f (f (f x)) x = f . f . f . f . f . f . f . f . f . f . f $ x -- etc.так что фокус в том, чтобы закончить,
fнужно сгенерировать вид структуры, так что позжеfможет ли шаблон соответствовать этой структуре и завершить рекурсию, фактически не заботясь о полном "значении" своего параметра (?)если, конечно, вы не хотите сделать что-то вроде создания бесконечного списка, как проиллюстрировал лукви.
факторное объяснение Томмда хорошо. Подпись типа ремонта составляет
(a -> a) -> a. Подпись типа для(\recurse d -> if d > 0 then d * (recurse (d-1)) else 1)и(b -> b) -> b -> bиными словами,(b -> b) -> (b -> b). Так что можно сказать, чтоa = (b -> b). Таким образом, исправить принимает нашу функцию, котораяa -> a, или действительно,(b -> b) -> (b -> b), и вернет результат типаaиными словами,b -> b, другими словами, другая функция!Подождите, я думал, что он должен был вернуть фиксированную точку...это не функция. Ну это так, вроде (так как функции-это данные). Вы можете себе представить, что это дало нам окончательную функцию для нахождения факториала. Мы дали ему функцию, которая не знает, как рекурсировать (следовательно, один из параметров к ней является функцией, используемой рекурсивно), и
fixнаучил ее повторить.помните, как я сказал, что
fдолжен генерировать какую-то структуру, чтобы позжеfможет ли шаблон совпадать и завершаться? Ну, это не совсем так, я думаю. TomMD проиллюстрировал, как мы можем расширитьxчтобы применить функцию и шаг к базовому случаю. Для своей функции он использовал if / then, и это то, что вызывает прекращение. После повторных замен,inчасть всего определенияfixв конечном итоге перестает определяться в терминахxи это когда он вычислим и полон.
вам нужен способ для завершения точки фиксации. Расширение вашего примера очевидно, что он не закончит:
fix id --> let x = id x in x --> id x --> id (id x) --> id (id (id x)) --> ...вот реальный пример того, как я использую fix (обратите внимание, что я не использую fix часто и, вероятно, устал / не беспокоился о читаемом коде, когда писал это):
(fix (\f h -> if (pred h) then f (mutate h) else h)) qWTF, вы говорите! Ну, да, но здесь есть несколько действительно полезных моментов. Прежде всего, ваш первый
fixаргумент должен быть функцией, которая в случае 'рекурсия' и второй аргумент-это данные, по которым действовать. Вот тот же код, что и именованная функция:getQ h | pred h = getQ (mutate h) | otherwise = hесли вы все еще смущены, то, возможно, факториал будет более простым примером:
fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 5 -->* 120обратите внимание на оценки:
fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 3 --> let x = (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x in x 3 --> let x = ... in (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 3 --> let x = ... in (\d -> if d > 0 then d * (x (d-1)) else 1) 3О, ты видел это? Это
xстал функцией внутри нашегоthenфилиала.let x = ... in if 3 > 0 then 3 * (x (3 - 1)) else 1) --> let x = ... in 3 * x 2 --> let x = ... in 3 * (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 2 -->выше вы должны помнить
x = f x, отсюда два аргументаx 2в конце вместо просто2.let x = ... in 3 * (\d -> if d > 0 then d * (x (d-1)) else 1) 2 -->и я остановлюсь здесь!
одно четкое определение вы могли бы повторно изобрели исправить тоже!
как я понимаю, он находит значение для функции, так что он выводит то же самое, что вы ему даете. Уловка заключается в том, что он всегда будет выбирать неопределенный (или бесконечный цикл, в haskell, неопределенные и бесконечные циклы одинаковы) или то, что имеет в нем самые неопределенные. Например, с id,
λ <*Main Data.Function>: id undefined *** Exception: Prelude.undefinedкак вы можете видеть, undefined является фиксированной точкой, поэтому
fixвернусь к этому. Если вы вместо этого делаете (\x - >1: x).λ <*Main Data.Function>: undefined *** Exception: Prelude.undefined λ <*Main Data.Function>: (\x->1:x) undefined [1*** Exception: Prelude.undefinedтак
fixне могу выбрать неопределенным. Чтобы сделать его немного более связанным с бесконечными циклами.λ <*Main Data.Function>: let y=y in y ^CInterrupted. λ <*Main Data.Function>: (\x->1:x) (let y=y in y) [1^CInterrupted.опять же, небольшая разница. Так что же такое фиксированная точка? Давайте попробуем
repeat 1.λ <*Main Data.Function>: repeat 1 [1,1,1,1,1,1, and so on λ <*Main Data.Function>: (\x->1:x) $ repeat 1 [1,1,1,1,1,1, and so onэто то же самое! Так как это единственная неподвижная точка,
fixдолжен остановиться на нем. Извинитеfix, никаких бесконечных циклов или неопределенных для вас.
Comments