18

Reddit 上的一个人让我注意到了这段代码:

main = do
  let ns = [print 1, print 2, print 3]
  sequence_ ns
  sequence_ $ reverse ns
  sequence_ $ tail ns ++ [head ns]
  head ns

这里发生的事情是我们有一系列操作,我们可以用它们来做一些事情,比如反向或获取它的尾部或头部。

惊人的。

我想做的是进入单个元素并永久改变它们。例如,我希望能够做这样的事情:

ns !! 0

并得到类似 [print, 1] 的内容,然后将最后一个元素更改为 3.14,以便函数打印 3.14。

在 Haskell 中是否有可能,还是我应该回到 LISP?

一个重要的编辑:我有点失误。我了解我需要创建一个新列表。是否可以获取作为列表一部分的函数的参数?我想要的是能够从它们的标识符/参数组合函数,并且还能够在评估之前将函数分解为标识符/参数。

4

4 回答 4

11

一旦你给一个函数应用了一个值,就没有办法取回它了。尝试将函数及其参数包装在可以根据需要进行评估或分解的数据类型中。

data App a b = App (a -> b) a
runApp (App a b) = a b
ns = [App print 1, App print 2, App print 3]
main = do
    sequence_ $ map runApp ns
    let ns2 = [App fun (arg^2) | App fun arg <- ns]
    sequence_ $ map runApp ns2

输出

1
2
3
1
4
9
于 2009-08-18T20:13:20.240 回答
11

它比 Lisp 中的要复杂一些,但是对于 Haskell 中的元编程,您可以使用Template Haskell

例如,[|print 1|]将被翻译为

return $ AppE (VarE $ mkName "print") (LitE $ IntegerL 1)

它具有类型Q Exp(表达式的引用)。

如果要将自己的数据拼接成引用,[|print $(foo 3.14)|]foo 3.14在编译时执行。

于 2009-08-19T09:06:52.177 回答
4

你想改变列表吗?你应该回到 lisp ;-)

Haskell 中的值是不可变的。Haskell 方法是创建一个新列表,除了最后一个元素外,它与旧列表等效。

(有一些涉及 monad 的技巧,您可以在其中模拟可变值和指针,但这可能不是您想要的。)

编辑:不完全确定我理解编辑过的问题,但您可以将函数和参数分别作为数据处理,然后稍后“应用”,例如;

do
    let ns = [(print, 1), (print, 2), (print, 3)]
    sequence_ $ map (\(f,a)->f a) ns
于 2009-08-18T18:57:20.673 回答
1

就像已经说过的那样,haskell 方法只是创建一个新列表,但是如果你真的想要的话,你可以在 IO monad 中使用 IOArray 拥有可变数组

import Data.Array.IO

seqArr_ arr = getElems arr>>=sequence_

main= do
  arr <- newListArray (0,2) [print 1,print 2,print 3] :: IO (IOArray Int (IO ()))
  seqArr_ arr  -- prints 1 2 3
  writeArray arr 2 (print 3.14) -- change the last element
  seqArr_ arr  -- prints 1 2 3.14
于 2009-08-19T01:52:58.350 回答