如何在haskell中使用递归函数来查找列表的第n个元素?和更多

时间:2015-10-28 13:18:49

标签: haskell functional-programming

我最近开始在Haskell中开始学习函数式编程,并且遇到了一些需要解决的问题,它要求使用递归和基本系统函数为列表创建自己的某些系统函数版本。我需要写的功能是:

  • !!(列表中的第n项)
  • append(加入名单)
  • subst(替代),例如subst 'x' 'y' ['q','x','r','x','s']〜> ['q','y','r','x','y','s']
  • intersection例如intersection [2,5,7] [9,7,3,5]〜> [5,7]
  • union union union [2,5,7] [9,7,3,5]〜> [2,5,7,9,3]
  • reverse例如reverse [4,5,6,7]〜> [7,6,5,4]

我从第一个开始,并写了这样的定义:

nthelement :: Eq a => [a] -> a -> a

在命令式语言中我会创建一个计数器变量(比如i)并使用系统函数tail删除列表的第一个元素,直到i = n。但是当我了解到在功能上你只能做常量我不能想出一种方法来决定何时停止重复并返回元素而不是重新调用tail函数直到列表为空。

请帮我解决这个问题。做第一个功能或任何一个功能的任何帮助都会非常好。感谢。

2 个答案:

答案 0 :(得分:9)

(正如@Bergi在评论中正确指出的那样,你应该检查你的签名和标准库中!!的签名是否重合!)

不要将i中的list !! i视为变量,而应将其视为函数参数。然后,考虑此参数的不同情况,并确定!!函数在每种情况下应执行的操作。然后,考虑列表参数list有哪些选项,以及如何考虑它们:

扩展ilist的所有潜在选项可为我们提供以下内容:

nthelement :: [a] -> Integer -> a
nthelement [] 0 = -- ?
nthelement [] i = -- ?
nthelement (l:ls) 0 = -- ?
nthelement (l:ls) n = -- ?

其余功能可以按照类似的策略编写。

答案 1 :(得分:1)

我只是对你提出的一个问题提供一般性答案。我希望这能帮助您更好地理解一般问题,并为您的具体问题/任务提出解决方案。

  

在命令式语言中我会创建一个计数器变量(比如说i)并使用系统函数tail来删除列表的第一个元素,直到i = n。但是当我了解到在功能上你只能做常量时我无法想出一种方法来决定何时停止重复并返回元素而不是重新调整尾部函数直到列表为空。

你总是可以使用状态变量“模拟”一个for循环,并使用递归来突破循环:

的Python:

def foo(xs):
    state = initial_state
    for x in xs:
        state = make_new_state(x, state)
        if not condition(state, x):
            break
    return state

在等效的Haskell代码中,您将使用内部函数和状态的额外参数。您还可以在foo上公开额外参数,但通常您不希望将其公开给foo的调用者:

foo xs = go initialState xs
  where go []     state = state
        go (x:xs) state = if not (condition state x)
                          then state
                          else go xs (makeNewState x state)

对于许多算法,根本不需要突破循环,在这种情况下,“模式”变为:

foo xs = go initialState xs
  where go []     state = state
        go (x:xs) state = go xs (makeNewState x state)

其中makeNewState是你在每一步执行的逻辑(当然,它不必在一个单独的函数中)。

对于后一种情况,有一些通用函数foldrfoldl以及foldl',这样:

foo xs = foldr makeNewState initialState xs

此外:还有像State monad这样的东西,可以让你以纯粹的方式编写命令式逻辑,但最好先理解“原始”递归和折叠之类的事情,然后才转向monad和显式状态等事情。