这个C代码在概念上可以描述为创建一个与输入数组相同但以1为第一个元素的新数组:
int* retire_and_update(int* arr) {
arr[0] = 1;
return arr;
}
这是一个纯函数(wink wink nudge nudge),只要不再对输入数组及其元素进行引用即可。 C类型系统不会为我们强制执行,但它原则上似乎是可执行的。
gcc生成的代码简单而有效:
retire_and_update:
movq %rdi, %rax
movl $1, (%rdi)
ret
我们的功能通过在恒定时间内创建一个全新的数组并且不使用额外的内存来实现看似不可能。尼斯。可以使用类似代码有效地实现具有类似数组的输入和输出的Haskell函数吗?有没有办法表达"这是对这个变量的最后一次引用"这样一个纯函数可以在幕后蚕食变量吗?
如果函数被内联,那么在这里没有任何有趣的事情需要发生,所以让我们假设调用者和函数将被单独编译。
答案 0 :(得分:6)
虽然ST monad
不正是您描述的内容,但实际上您可以使用STUArray
实现大部分内容。因此,模拟您的代码可能类似于:
import Control.Monad (forM_)
import Control.Monad.ST (ST)
import Data.Array.Unboxed (UArray)
import Data.Array.ST (STUArray, newArray, readArray, writeArray, runSTUArray)
retire_and_update :: STUArray s Int Int -> ST s (STUArray s Int Int)
retire_and_update arr = do
writeArray arr 0 1
return arr
如果你有另一个函数可以就地修改数组,比如:
mutate_inplace :: STUArray s Int Int -> Int -> ST s ()
mutate_inplace arr size = do
forM_ [2..size - 1] $ \i -> do
a <- readArray arr (i - 2)
b <- readArray arr (i - 1)
writeArray arr i (a + b)
您可以绑定两个不纯的函数,并使用runSTUArray
在纯函数中调用它们:
run :: Int -> UArray Int Int
run size = runSTUArray $ do
arr <- newArray (0, size - 1) 0
retire_and_update arr
mutate_inplace arr size
return arr
请注意run
保持纯粹,返回数组的早期版本不会泄露到任何地方:
\> run 8
array (0,7) [(0,1),(1,0),(2,1),(3,1),(4,2),(5,3),(6,5),(7,8)]