我有一个函数,将采取和int并返回其平方根.但是现在我想修改它,以便它需要一个整数数组并返回一个数组,其中包含第一个数组元素的平方根.我知道Haskell不使用循环所以如何进行这种修改?谢谢.
intSquareRoot :: Int -> Int
intSquareRoot n = try n where
try i | i*i > n = try (i - 1)
| i*i <= n = i
Run Code Online (Sandbox Code Playgroud)
"循环一些集合"的想法,将每个结果放在其输入的相应插槽中,这是一个有点微不足道,非常常见的模式.模式适用于OO程序员.在Haskell中,当有一个模式时,我们想要对它进行抽象,即给它一个简单的名称,我们总是可以重复使用而无需额外的样板.
这种特殊的"模式"是仿函数操作1.对于列表,它被称为
map :: (a->b) -> [a]->[b]
Run Code Online (Sandbox Code Playgroud)
更一般地说(例如,它也适用于真正的数组;列表实际上不是数组),
class Functor f where
fmap :: (a->b) -> f a->f b
Run Code Online (Sandbox Code Playgroud)
因此,而不是定义一个额外的功能
intListSquareRoot :: [Int] -> [Int]
intListSquareRoot = ...
Run Code Online (Sandbox Code Playgroud)
你只需使用map intSquareRoot你想要使用该功能的地方.
当然,您也可以定义"提升"版本intSquareRoot,
intListSquareRoot = map intSquareRoot
Run Code Online (Sandbox Code Playgroud)
但这几乎没有什么可以简单地map在你需要的地方内联呼叫.
那就是说...当然有理由怀疑它map本身是如何运作的.好吧,您可以通过递归手动"循环"列表:
map' :: (a->b) -> [a]->[b]
map' _ [] = []
map' f (x:xs) = f x : map' f xs
Run Code Online (Sandbox Code Playgroud)
现在,您可以在此处内联您的特定功能
intListSquareRoot' :: [Int] -> [Int]
intListSquareRoot' [] = []
intListSquareRoot' (x:xs) = intSquareRoot x : intListSquareRoot' xs
Run Code Online (Sandbox Code Playgroud)
这不仅比快速插入map魔术词更笨拙和笨拙,它通常也会更慢:GHC等编译器在处理更高级别的概念2(例如折叠)时可以做出更好的优化,而不是必须工作时一次又一次地使用手动定义的递归.
1 不要混淆许多C++程序员称之为" 仿函数 "的东西.Haskell使用正确的数学意义上的单词,来自类别理论.
2 这就是为什么像Matlab和APL这样的语言实际上为特殊应用程序实现了不错的性能,尽管它们是动态类型的解释语言:它们将这种特殊的"向量循环"硬件编码到它们的语法中.(不幸的是,这几乎是他们唯一能做得好的......)