我正在努力理解"Löb and möb: strange loops in Haskell",但是现在我的意思正在逐渐消失,我只是不明白为什么它会有用。只是召回函数loeb
定义为
loeb :: Functor f => f (f a -> a) -> f a
loeb x = go where go = fmap ($ go) x
或等效地:
loeb x = go
where go = fmap (\z -> z go) x
在文章中有一个带有[]
仿函数和电子表格实现的示例,但对于我来说,就像电子表格本身一样(从未使用它们)对我来说有点陌生。
虽然我理解电子表格的内容,但我认为尽管有列表,但对我和其他人来说有更多的例子会有所帮助。 loeb
或其他仿函数是否有Maybe
申请?
答案 0 :(得分:18)
loeb
的主要来源(我认为)是Dan Piponi's blog, A Neighborhood of Infinity。他在那里更详细地解释了整个概念。我将复制一点作为答案,并添加一些例子。
loeb
实现了一种奇怪的延迟递归
loeb :: Functor a => a (a x -> x) -> a x
loeb x = fmap (\a -> a (loeb x)) x
我们假设我们有一个a
类型,其中Functor a
和a
- 代数(类型为a x -> x
的函数)。您可能会认为这是一种从值结构中计算值的方法。例如,这里有一些[]
- 代数:
length :: [Int] -> Int
(!! 3) :: [a] -> a
const 3 :: Num a => [a] -> a
\l -> l !! 2 + l !! 3 :: Num a => [a] -> a
我们可以看到这些a
- 代数可以使用Functor
中存储的值和Functor
本身的结构。
另一种思考d :: a x -> x
的方法是x
的值,它需要一些上下文 - 整个Functor
化值a x
- 才能进行计算。也许这种解释更清楚地写成Reader (a x) x
,强调这只是x
的值,它被推迟,等待生成a x
上下文。
type Delay q x = q -> x
使用这些想法,我们可以如下描述loeb
。我们获得了一个f
- 结构,其中包含Delay
个f
个值,其中Functor
为Functor f, f (Delay q x)
q
当然,如果我们获得force :: Functor f => f (Delay q x) -> q -> f x
force f q = fmap ($ q) f
,那么我们可以将其转换为非延迟形式。实际上,只有一个(非作弊)函数可以多态地执行此操作:
loeb
q
做的是处理force f q
实际为fix
的额外棘手案例,这是此函数的结果。如果您熟悉loeb :: Functor a => a (Delay (a x) x) -> a x
loeb f = fix (force f)
,这正是我们如何产生这种结果的。
Delay
因此,举个例子,我们只需要构建一个包含> loeb [ length :: [Int] -> Int
, const 3 :: [Int] -> Int
, const 5 :: [Int] -> Int
, (!! 2) :: [Int] -> Int
, (\l -> l !! 2 + l !! 3) :: [Int] -> Int
]
[5, 3, 5, 5, 10]
ed值的结构。一个自然的例子是使用之前的列表示例
const 3
在这里我们可以看到列表中充满了延迟等待评估列表结果的值。这个计算可以完全进行,因为数据依赖中没有循环,所以整个事情可以懒得确定。例如,const 5
和length
都可以立即作为值使用。 (!! 2)
要求我们知道列表的长度,但不包含任何值,因此它也会立即在我们的固定长度列表中进行。有趣的是延迟等待来自结果列表内部的其他值的值,但是因为const 5
仅取决于结果列表的第三个值,这由(\l -> l !! 2 + l !! 3)
确定,因此可以是立即可用,计算向前移动。 loeb
也会出现同样的想法。
所以你有它:Functor
完成这种奇怪的延迟值递归。不过,我们可以在任何类型的Delay
上使用它。我们需要做的就是考虑一些有用的Maybe
ed值。
Chris Kuklewicz的评论指出,你可以用Maybe
作为你的函子来做很多有趣的事情。这是因为maybe (default :: a) (f :: a -> a) :: Maybe a -> a
以上的所有延迟值都采用
Maybe (Delay (Maybe a) a)
并且Just (maybe default f)
的所有有趣值都应该是loeb Nothing = Nothing
,因为default
。所以在一天结束时,loeb (Just (maybe default f)) == fix f
值永远不会被使用 - 我们总是只有那个
{{1}}
所以我们也可以直接写下来。
答案 1 :(得分:1)
以下是一个实例,用于:Map String Float
http://tryplayg.herokuapp.com/try/spreadsheet.hs/edit
具有循环检测和循环分辨率。
该程序计算速度,时间和空间。每一个都取决于其他两个。每个单元格都有两个值:他当前输入的值和表达式作为其他单元格值/表达式的函数。允许循环。
Cell重新计算代码使用了Dan Piponi在2006年的着名loeb表达式。到目前为止,根据我的知识,这个公式在实际工作电子表格上没有任何具体化。这一个接近它。由于loeb在使用循环表达式时进入无限循环,程序会对循环进行计数,并通过逐步用单元格值替换公式来降低复杂性,直到表达式没有循环
此程序配置为在单元格更改时立即重新计算,但可以通过按钮触发它来调整以允许在重新计算之前修改多个单元格。
这是博客pos:
http://haskell-web.blogspot.com.es/2014/09/spreadsheet-like-program-in-browser.html
答案 2 :(得分:1)
您可以将它用于动态编程。我想到的例子是Smith-Waterman algorithm。
import Data.Array
import Data.List
import Control.Monad
data Base = T | C | A | G deriving (Eq,Show)
data Diff = Sub Base Base | Id Base | Del Base | Ins Base deriving (Eq,Show)
loeb x = let go = fmap ($ go) x in go
s a b = if a == b then 1 else 0
smithWaterman a' b' = let
[al,bl] = map length [a',b']
[a,b] = zipWith (\l s -> array (1,s) $ zip [1..] l) [a',b'] [al,bl]
h = loeb $ array ((0,0),(al,bl)) $
[((x,0),const 0) | x <- [0 .. al]] ++
[((0,y),const 0) | y <- [1 .. bl]] ++
[((x,y),\h' -> maximum [
0,
(h' ! (x - 1,y - 1)) + s (a ! x) (b ! y),
(h' ! (x - 1, y)) + 1,
(h' ! (x, y - 1)) + 1
]
) | x <- [1 .. al], y <- [1 .. bl]]
ml l (0,0) = l
ml l (x,0) = ml (Del (a ! x): l) (x - 1, 0)
ml l (0,y) = ml (Ins (b ! y): l) (0, y - 1)
ml l (x,y) = let
(p,e) = maximumBy ((`ap` snd) . (. fst) . (const .) . (. (h !)) . compare . (h !) . fst) [
((x - 1,y),Del (a ! x)),
((y, x - 1),Ins (b ! y)),
((y - 1, x - 1),if a ! x == b ! y then Id (a ! x) else Sub (a ! x) (b ! y))
]
in ml (e : l) p
in ml [] (al,bl)