跳转到内容

另一个 Haskell 教程/语言基础/解决方案

来自维基教科书,开放的世界,开放的书籍
Haskell
另一个 Haskell 教程
前言
介绍
入门
语言基础 (解决方案)
类型基础 (解决方案)
IO (解决方案)
模块 (解决方案)
高级语言 (解决方案)
高级类型 (解决方案)
单子 (解决方案)
高级 IO
递归
复杂度

它绑定得更紧密;实际上,函数应用绑定得比任何其他东西都紧密。为了看到这一点,我们可以做一些事情,例如

示例

Prelude> sqrt 3 * 3
5.19615

如果乘法绑定得更紧密,结果将是 3。

对、三元组及更多

[编辑 | 编辑源代码]

解决方案:snd (fst ((1,'a'),"foo"))。这是因为我们首先要取元组的前一半:(1,'a'),然后从中取后一半,得到'a'

如果你尝试了fst (snd ((1,'a'),"foo")),你会得到一个类型错误。这是因为snd的应用会留下fst "foo"。然而,字符串"foo"不是元组,所以你不能将fst应用于它。

字符串

[编辑 | 编辑源代码]

简单列表函数

[编辑 | 编辑源代码]

解决方案:map Data.Char.isLower "aBCde"

解决方案:length (filter Data.Char.isLower "aBCde")

解决方案:foldr max 0 [5,10,2,8,1]

你也可以使用foldlfoldr的情况更容易解释:我们将每个cons替换为max的应用,并将空列表替换为 0。因此,最内层的应用将取 0 和列表的最后一个元素(如果存在)的最大值。然后,下一个内层的应用将返回之前最大值和倒数第二个元素的最大值。这将持续下去,将当前最大值一直带回列表的开头。

foldl的情况下,我们可以将其视为按顺序查看列表中的每个元素。我们从"状态" 0 开始。我们取出第一个元素并检查它是否大于我们当前的状态。如果是,我们用该数字替换我们当前的状态并继续。这会针对每个元素发生,因此最终会返回最大元素。

解决方案:fst (head (tail [(5,'b'),(1,'c'),(6,'a')]))

源代码文件

[编辑 | 编辑源代码]

let 绑定

[编辑 | 编辑源代码]

我们可以将斐波那契函数定义为

fib 1 = 1
fib 2 = 1
fib n = fib (n-1) + fib (n-2)

我们也可以使用显式的if语句来编写它,例如

fib n =
  if n == 1 || n == 2
    then 1
    else fib (n-1) + fib (n-2)

两种方式都可接受,但第一种在 Haskell 中可能更自然。

我们可以定义

然后输入代码

mult a 0 = 0
mult a 1 = a
mult a b = 
    if b < 0
        then 0 - mult a (-b)
        else a + mult a (b-1)

需要注意的是, 我们进行递归操作,并不重要。我们可以同样地定义它为:

mult 0 b = 0
mult 1 b = b
mult a b = 
    if a < 0
        then 0 - mult (-a) b
        else b + mult (a-1) b

我们可以定义 my_map

my_map f [] = []
my_map f (x:xs) = f x : my_map f xs

回顾一下,my_map 函数应该将函数 f 应用于列表中的每个元素。如果列表为空,则没有元素可应用函数,因此我们只返回空列表。

如果列表非空,则它是一个元素 x 后面跟着一个列表 xs。假设我们已经正确地将 my_map 应用于 xs,那么我们只需要将 f 应用于 x,然后将结果组合在一起。这正是第二行所做的。

交互性

[编辑 | 编辑源代码]

下面的代码出现在 Numbers.hs 中。唯一棘手的部分是 getNumsshowFactorials 中的递归调用。

module Main
    where

import System.IO

main = do
  nums <- getNums
  putStrLn ("The sum is " ++ show (sum nums))
  putStrLn ("The product is " ++ show (product nums))
  showFactorials nums

getNums = do
  putStrLn "Give me a number (or 0 to stop):"
  num <- getLine
  if read num == 0
    then return []
    else do rest <- getNums
            return ((read num :: Int):rest)

showFactorials []     = return ()
showFactorials (x:xs) = do
  putStrLn (show x ++ " factorial is " ++
            show (factorial x))
  showFactorials xs

factorial 1 = 1
factorial n = n * factorial (n-1)

getNums 的想法就像提示中所述。对于 showFactorials,我们首先考虑递归调用。假设我们有一个数字列表,第一个数字是 x。首先,我们打印出显示阶乘的字符串。然后我们打印出其余部分,因此进行递归调用。但是在空列表的情况下我们应该怎么做?显然我们已经完成了,所以我们不需要做任何事情,所以我们简单地 return ()

注意,这必须是 return () 而不是 (),因为如果我们简单地写 showFactorials [] = (),那么它将不是一个 IO 操作,因为它必须是。为了更清楚地说明这一点,你可能应该继续阅读教程。

华夏公益教科书