Haskell 对“;固定和#x201D;关于非函数类型?

Haskell 对“;固定和#x201D;关于非函数类型?,haskell,recursion,fixpoint-combinators,Haskell,Recursion,Fixpoint Combinators,每次我使用fix::(a->a)->a,它都是在 ((a -> b) -> a -> b) -> a -> b 对于某些a和b。除了像fix(const 0)这样的琐碎事情之外,fix的某些应用程序的类型参数没有被实例化为函数类型吗?保留签名最一般的目的是什么?我不知道您是否会认为这个示例很琐碎,但您可以直接使用fix来建立数据: repeat :: a -> [a] repeat x = fix (x:) 例如,斐波那契序列: fibs = fix ((

每次我使用
fix::(a->a)->a
,它都是在

((a -> b) -> a -> b) -> a -> b

对于某些
a
b
。除了像
fix(const 0)
这样的琐碎事情之外,
fix
的某些应用程序的类型参数没有被实例化为函数类型吗?保留签名最一般的目的是什么?

我不知道您是否会认为这个示例很琐碎,但您可以直接使用
fix
来建立数据:

repeat :: a -> [a]
repeat x = fix (x:)

例如,斐波那契序列:

fibs = fix ((1:) . (1:) . (zipWith (+) <*> tail))
或斐波那契序列的另一种变体:

fibs :: State (Int, Int) [Int]
fibs = fix $ \loop -> do
    (x, y) <- get
    put (y, y + x)
    (x :) <$> loop

main = print $ take 15 $ fst $ runState fibs (1, 1)
fibs::State(Int,Int)[Int]
fibs=fix$\loop->do

(x,y)有许多使用
fix
构建corecursive数据的示例。我不知道如何详细阐述一般理论,但似乎任何类似于流的数据类型都可以通过
fix
计算,因为到目前为止,只要给定流,就可以输出一个以上的值,而无需向其提供函数类型

例子 例如,最简单的例子(在Cactus的答案中给出)是一个重复的值流

x = [1, 1, 1, 1, 1, 1, 1, 1, ...]
这满足方程

(1:) x = x
0 : map (+1) n = n
并且可以由

>> fix (1:)
[1,1,1,1,1,1,1,1,1,1,...]
>> fix ((0:) . map (+1))
[0,1,2,3,4,5,6,7,8,9,...]

一个稍微复杂一点的例子是自然数

n = [0, 1, 2, 3, 4, 5, 6, ...]
满足这个方程

(1:) x = x
0 : map (+1) n = n
并且可以由

>> fix (1:)
[1,1,1,1,1,1,1,1,1,1,...]
>> fix ((0:) . map (+1))
[0,1,2,3,4,5,6,7,8,9,...]

如果我们查看成对的
(n,f)
,其中
f
n
第个阶乘数,则最容易生成阶乘数-

x = [(0,1), (1,1), (2,2), (3,6), (4,24), (5,120), ...]
如果我们将成对的
(n,f)
取为
(n+1,f*(n+1))
,然后将cons
(0,1)
取为列表的开头,则它们是固定的。因此,它们可以由

>> fix $ \xs -> (0,1) : map (\(n,f) -> (n+1,f*(n+1))) xs
[(0,1),(1,1),(2,2),(3,6),(4,24),(5,120),(6,720),(7,5040),...]
斐波那契数可以类似地生成,如user3237465的答案

概括例子 这里的所有三个示例本质上都是转换为corecursive流的递归函数,即它们具有一些初始状态
s
,流发出的值对于某些函数
f
fs
f(fs)
等。执行此操作的一般方法是函数
iterate

iterate :: (a -> a) -> a -> [a]
iterate f x = x : iterate f (f x)
可根据
fix
-

iterate f x = x : map f (iterate f x)
            = (x:) . (map f) $ iterate f x
            = fix ((x:) . map f)
因此,任何重复将函数应用于某个状态的流都可以用
fix
来编写(当然,您可以简单地使用
iterate
而不是
fix
——这是规则的一种特殊情况,
fix
在允许递归let表达式的语言中是不必要的)

非流示例

对于一个不是流的例子,考虑二叉树在分支中的值-< /p>

data Tree a = Tip | Bin a (Tree a) (Tree a) deriving (Show)
如果我们想要一个二叉树,其节点是按广度优先顺序标记的,请注意,我们可以通过获取自身的两个副本,并将左分支和右分支中的所有值增加适当的量来修复这样的树,如以下函数所定义-

fun :: (Num a) => Tree a -> Tree a
fun t = Bin 1 (incr 1 t) (incr 2 t)
  where
    incr n (Bin a l r) = Bin (a+n) (incr m l) (incr m r)
      where
        m = 2 * n
使用一个简单的函数
takeLevels
只显示树的初始部分,然后计算固定点

>> takeLevels 3 $ fix fun
Bin 1 (Bin 2 (Bin 4 Tip Tip) (Bin 5 Tip Tip)) (Bin 3 (Bin 6 Tip Tip) (Bin 7 Tip Tip))

这正是我们想要的。

“保留最一般的签名的目的是什么?”这样做不会造成任何损失。我真的很喜欢这些示例,尽管我觉得第二个示例有点作弊,因为
State
本质上是
s->(a,s)
的同义词,因此提供给fix的输入具有类型
((b->c)->b->c)
其中
b=(Int,Int)
c=((Int,Int),[Int])
@Chris Taylor,这很公平。然而,其中一个问题是“留下签名最一般的目的是什么?”,这个例子就是说明。@ChrisTaylor:对。同样,任何类型都可以被理解为“本质上是”这样一个函数类型的同义词,即它的非类型lambda演算表示。阶乘数只是
fix((1:).zipWith(*)[1..)