如何修改此Haskell平方根函数以获取数组

Cha*_*upa 0 recursion haskell

我有一个函数,将采取和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)

lef*_*out 8

别.

"循环一些集合"的想法,将每个结果放在其输入的相应插槽中,这是一个有点微不足道,非常常见的模式.模式适用于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这样的语言实际上为特殊应用程序实现了不错的性能,尽管它们是动态类型的解释语言:它们将这种特殊的"向量循环"硬件编码到它们的语法中.(不幸的是,这几乎是他们唯一能做得好的......)

  • @Chalupa这个答案表明你应该_not_定义`intListSquareRoot` - 你可以直接使用原始函数和`map`:例如`map intSquareRoot [16,36,25]`返回`[4,6,5 ]`. (3认同)
  • @Chalupa你已经定义了`intSquareRoot`.不要管它.如果你需要*改变*你已经做过的其他事情,你可能做错了什么.问问自己,`intSquareRoot`的设计有什么问题吗?不,它尽可能简单; 这没什么不对.这意味着你应该寻找一种方法来重用它而不是试图*改变它. (3认同)