我如何将循环写为lambda函数?

时间:2013-02-19 22:41:31

标签: haskell

只是为了好玩,这是我自己的cycle版本:

myCycle :: [a] -> [a]
myCycle xs = xs ++ myCycle xs

右侧是指函数名称myCycle和参数xs

是否可以在提及myCyclemyCycle的情况下实施xs

myCycle = magicLambdaFunction

4 个答案:

答案 0 :(得分:11)

  

是否可以在不提及右侧的myCyclemyCycle的情况下实施xs

答案是肯定而不是(不一定按顺序)。

其他人提到了定点组合器。如果您有一个定点组合fix :: (a -> a) -> a,那么正如您在对Pubby的回答的评论中提到的那样,您可以写myCycle = fix . (++)

fix的标准定义是:

fix :: (a -> a) -> a
fix f = let r = f r in r

-- or alternatively, but less efficient:
fix' f = f (fix' f)

请注意,fix的定义涉及在其定义的右侧提及左侧变量(第一个定义中为r,第二个定义中为fix' )。所以我们到目前为止所做的就是将问题推迟到fix

值得注意的是,Haskell基于类型化的lambda演算,并且出于技术上的原因,大多数类型的lambda演算被设计为使得不能“本地”表达固定点组合。如果您在基础演算的“顶部”添加一些允许计算固定点的额外功能,这些语言只会变成图灵完备。例如,其中任何一个都可以:

  1. fix作为基元添加到微积分中。
  2. 添加递归数据类型(Haskell具有;这是在Haskell中定义fix的另一种方式)。
  3. 允许定义引用定义的左侧标识符(Haskell也有)。
  4. 这是一种有用的模块化类型,原因有很多 - 一种是没有固定点的lambda演算也是逻辑的一致证明系统,另一种是fix - 许多此类系统中的较少程序可以证明终止。


    编辑:这是用递归类型编写的fix。现在fix本身的定义不是递归的,但Rec类型的定义是:

    -- | The 'Rec' type is an isomorphism between @Rec a@ and @Rec a -> a@:
    --
    -- > In  :: (Rec a -> a) -> Rec a
    -- > out :: Rec a        -> (Rec a -> a)
    --
    -- In simpler words:
    --
    -- 1. Haskell's type system doesn't allow a function to be applied to itself.
    --
    -- 2. @Rec a@ is the type of things that can be turned into a function that
    --    takes @Rec a@ arguments.
    --
    -- 3. If you have @foo :: Rec a@, you can apply @foo@ to itself by doing
    --    @out foo foo :: a@.  And if you have @bar :: Rec a -> a@, you can do 
    --    @bar (In bar)@.
    --
    newtype Rec a = In { out :: Rec a -> a }
    
    -- | This version of 'fix' is just the Y combinator, but using the 'Rec'
    -- type to get around Haskell's prohibition on self-application (see the
    -- expression @out x x@, which is @x@ applied to itself):
    fix :: (a -> a) -> a
    fix f = (\x -> f (out x x)) (In (\x -> f (out x x)))
    

答案 1 :(得分:6)

我认为这有效:

myCycle = \xs -> fix (xs ++)

http://en.wikipedia.org/wiki/Fixed-point_combinator

  

在支持匿名函数的编程语言中,定点组合器允许定义和使用匿名递归函数,即不必将这些函数绑定到标识符。在此设置中,定点组合器的使用有时称为匿名递归。

答案 2 :(得分:5)

为了好玩,这是另一回事:

let f = foldr (++) [] . repeat 

let f = foldr1 (++) . repeat

答案 3 :(得分:0)

还没有人指出修复解决方案的“明显”版本。我们的想法是将命名的递归调用转换为参数。

let realMyCycle = fix (\myCycle xs -> xs ++ myCycle xs)

这个引入技巧的“递归名称”几乎就是let in在Haskell中的作用。唯一的区别是使用内置构造更简单,可能更适合实现。