0

我正在将我的服务器端编程语言从 PHP 修改为 Haskell,因此我并不完全熟悉函数式语言设计。

在这里,我想将类似以下 php 函数的部分处理代码转换为 Haskell:

function loop($A) {
  $array1 = array();
  $num = (int)$A;
  if ($num == 0)
    $array1 = "0";
  for (int $i=0; $i<$num; $i++)
    foreach (loop($i-$num) as &$value)
      $array1[] = "+ ".$value." ";  //concat
  return $array1;
}

我想会大量使用 map/mapM_ 函数,但是累积将如何工作?我开始认为在这一点上增加的安全性不值得转型。

谢谢。

4

2 回答 2

6

这是我回答时的php:

function loop($A) {
  $array1 = array();
  $num = (int)$A;
  if ($num == 0)
    $array1 = "0";
  for (int $i=0; $i<$num; $i++)
    foreach (loop($i-$num) as &$value)
      $array1[] = "+ ".$value." ";  //concat
  return $array1;
}

将上述代码直译为 Haskell 将是:

loop :: Int -> [String]
loop 0 = ["0"]
loop num = ["+ " ++ value ++ " " | i <- [0..num], value <- loop (i - num)]

它仍然没有实现任何目标,但它确实给了您在列表推导中 Haskell 的“循环”或迭代的味道。

首先要注意的是,类型签名loop :: Int -> [String]立即提醒我写loop 0 = ["0"],而不是loop 0 = "0"更确切地说是你所拥有的,但 Haskell 发现如果循环的输出有时是字符串列表,有时只是一个字符串,它的输出会不一致. 我认为这是一种值得学习的错误检查-也许您在修改时编写的所有代码都会在php中编译,但是由于设计不一致,Haskell不会-它迫使您在编写时思考清楚并修复错误在编译时间之前。

我将解释我的代码,而不是作为对 Haskell 的完整解释,而是作为品尝者并将其与您的代码相关联:

loop :: Int -> [String]

loop是一个接受Integer 并返回字符串列表的函数。Haskell 现在不允许您将其与任何其他数据类型一起使用,这意味着它是类型安全的,并且您可以直接从盒子中得到保护,避免各种讨厌的错误。类型系统实际上非常灵活、富有表现力和强大,这也是我们喜欢 Haskell 的原因之一,但在这里我使用它来锁定功能。

(列表在 Haskell 中被广泛使用,而不是数组。它们非常方便并且可以快速地进行顺序访问。有数组数据类型,但它们不那么可爱/Haskellish - 首先习惯于列表。)

loop 0 = ["0"]

这意味着如果您loop使用 Int调用0,它应该返回带有一个字符串“0”的列表。

loop num = [ something | stuff... ]

意味着您可以在其中获取变量的值stuff并将它们返回something,因此

loop num = [ "+ " ++ value ++ " " | stuff... ]

意味着我们将返回 (Haskell 的版本) "+ ".$value." "。这里我们使用++而不是 php 的..

i <- [0..num]

这意味着 let irange from0num。它必须是Int因为num是因为loop :: Int -> ...

value <- loop (i - num)

这意味着 let valuerange 跨越loop使用 number调用的结果i - numvalue必须是String因为loop :: Int -> [String]

在 Haskell 中,就像递归调用被实现为表达式重写一样,因此它们在被调用时消失并且不会使堆栈混乱,除非您的计算本身会使堆栈变得混乱。

把它放在一起给出:

loop num = ["+ " ++ value ++ " " | i <- [0..num], value <- loop (i - num)]

如果这个符号感觉很奇怪,你可能会更喜欢命令式的语法:

loop :: Int -> [String]
loop 0 = ["0"]
loop num = do
    i <- [0..num]
    value <- loop (i - num)
    return ("+ " ++ value ++ " ")

当然它仍然没有做任何有用的事情,但至少它很容易跟踪。

为什么不通过h​​ttp: //learnyouahaskell.com/上的Learn You a Haskell for Great Goodhttp://book.realworldhaskell.org/上的Real World Haskell学习,这两个教程开始简单但深入。我认为您会发现 Haskell 是一种编写少量正确代码来替换大量 OK 代码的好方法。想得更清楚,写得更少!

于 2012-09-11T00:08:31.290 回答
3

看起来你有某种字典。字典的典型 Haskell 数据结构是由Map提供的Data.Map。因此,如果您的键是Strings 而您的值是Ints,那么您的 Map 将具有 type Map String Int

现在,从您的代码来看,您似乎将键和值混合在同一个数组中,因此首先我们必须将它们分成键值对。我会假装你的起始输入是一个列表而不是一个数组,尽管我在这里写的所有东西都适用于 Haskell 数组:

fix :: [a] -> [(a, a)]
fix [] = []
fix [_] = []
fix (k:v:xs) = (k, v):fix xs

您可以选择在关联列表处停止,在这种情况下,您可以将其转换为描述性字符串列表:

describe :: (Show k, Show v) => [(k, v)] -> [String]
describe xs = map (\(k, v) -> "Stored On: " ++ show k ++ ", item: " ++ show v) xs

或者我们可以直接打印出来:

printPairs :: (Show k, Show v) => [(k, v)] -> IO ()
printPairs xs = mapM_ putStrLn (describe xs)

现在,为了更有效的查找,您通常希望将关联列表存储为Map,因此您将使用的只是 中的fromList函数Data.Map

fromList :: Ord k => [(k, v)] -> Map k v

并且任何时候您想将列表从地图中取出(即打印它),您只需使用toList

toList :: Map k v -> [(k, v)]
于 2012-09-10T16:09:41.470 回答