我希望能在Haskell中做些什么,但如果可能的话,这一点并不明显。我是一个新手(例如,我还不了解monad),所以也许有一些先进的方法。
假设我已经将某个类型的函数f定义为自身。我希望能够定义一个概念“prev”,它代表列表中的前一个元素(给定类型的元素)。所以我想像[x,y,f prev]这样的意思是[x,y,f y]。我不介意定义是否涉及用自然数字压缩列表,只要我最后编写的内容可以隐藏构造方法。
这可能是不可能的一个原因是,如果有可能,那么一个人也应该能够定义“下一个”的概念,并且人们不希望能够写出[f next,g prev] 。如果不可能,还有下一个最好的选择吗?
答案 0 :(得分:5)
我不认为有一种方法可以引入一个新的关键字prev
来获得您描述的确切语法,至少缺少一些对这种情况来说显着过度的技术,比如隐式参数或模板Haskell。
但是,您可以使用名为tying the recursive knot的技术
来执行此操作type PrevList a = [a -> a]
instantiatePrevList :: PrevList a -> [a]
instantiatePrevList prevList =
let result =
zipWith (\f prev -> f prev)
prevList
(error "Initial element has no previous element":result)
in result
xs = instantiatePrevList [\_ -> 5, \_ -> 3, \prev -> prev + 1]
我们的想法是根据函数定义您的列表,这些列表总是传递上一个值 - 上面的PrevList
类型。如果您不需要特定元素,则可以选择忽略它。
然后我们需要一个函数将它们放在一起,这就是instantiatePrevList
所做的。
请注意,列表result
是根据自身定义的,这就是"打结"进来 - 它依赖于Haskell的懒惰来工作。
使用懒惰的另一种方式是第一个元素,它没有先前的元素。只要您不尝试使用列表第一个元素中的上一个值,就不会遇到错误。
正如你猜测的那样,同样的技术也可以用来定义一个next
- 只要你不写一些实际上依赖于它自己的东西,它仍然可以正常工作。终止方式。
编辑:
实际上,带有隐式参数的解决方案并不复杂,而且它确实可以更好地编写列表的语法:
{-# LANGUAGE ImplicitParams, ImpredicativeTypes, ScopedTypeVariables #-}
type PrevList a = [(?prev :: a) => a]
instantiatePrevList :: PrevList a -> [a]
instantiatePrevList prevList =
let result =
zipWith (\(elem :: ((?prev :: a) => a)) prev ->
let ?prev = prev in elem)
prevList
(error "Initial element has no previous element":result)
in result
xs = instantiatePrevList [5, 3, ?prev + 1]
你必须对它们有点小心,如果你试图嵌套它们,你可能会得到令人困惑的结果 - 例如在另一个PrevList
内加PrevList
。
答案 1 :(得分:2)
您可以定义自己的打结组合器以获得类似
的内容example :: [Int]
example = tie [ const 1, succ . prev, succ . prev, (*2) . next, const 100, const 20 ]
-- yields [ 1, 2, 3, 200, 100, 20 ]
直观地说,const value
表示value
是列表的元素。代替,
operation . prev
表示将operation
应用于上一个获取此元素
元件。表达式operation . next
的工作方式类似。
此外,您可以使用uncurry
e..g。
example2 :: [Int]
example2 = tie [ const 100, uncurry (+), const 5 ]
-- yields [ 100, 105, 5 ]
可能的实施是
tie :: [ (a,a) -> a ] -> [a]
tie = go (error "no previous element")
where go _ [] = []
go pr (f:fs) = this : rest
where rest = go this fs
this = f (pr, head rest)
prev :: (a,a) -> a
prev = fst
next :: (a,a) -> a
next = snd