Haskell-从末尾查找第k个元素,Haskell的元素与模式不匹配

Haskell-从末尾查找第k个元素,Haskell的元素与模式不匹配,haskell,Haskell,我试图实现一个递归函数,它从末尾返回k的元素 这是我的尝试: kElementFromEnd :: Int -> [x] -> x kElementFromEnd _ [] = error "cannot request k item from empty list" kElementFromEnd k [x] | k < 0 = error "k must be non negative" | k == 0 = last [x] | otherwis

我试图实现一个递归函数,它从末尾返回k的元素

这是我的尝试:

kElementFromEnd :: Int -> [x] -> x

kElementFromEnd _ [] = error "cannot request k item from empty list"
kElementFromEnd k [x]
    | k < 0 = error "k must be non negative"
    | k == 0 = last [x]
    | otherwise = kElementFromEnd (k-1) (init [x])
我真的不明白为什么haskell不能匹配这个模式。我不明白发生了什么事


谢谢

您只匹配了空列表[]和单元素列表[x]。我想你的意思是用简单的xs替换[x],一个匹配单个元素列表的模式,并将该值赋给x,这个模式匹配任何尚未匹配的列表。这看起来像

kElementFromEnd :: Int -> [x] -> x
-- This pattern matches the empty list
kElementFromEnd _ [] = error "cannot request k item from empty list"
-- This pattern is just a name, so it matches everything else
-- i.e. non-empty lists
kElementFromEnd k xs
    | k < 0 = error "k must be non negative"
    | k == 0 = last xs
    | otherwise = kElementFromEnd (k-1) (init xs)

您只匹配了空列表[]和单元素列表[x]。我想你的意思是用简单的xs替换[x],一个匹配单个元素列表的模式,并将该值赋给x,这个模式匹配任何尚未匹配的列表。这看起来像

kElementFromEnd :: Int -> [x] -> x
-- This pattern matches the empty list
kElementFromEnd _ [] = error "cannot request k item from empty list"
-- This pattern is just a name, so it matches everything else
-- i.e. non-empty lists
kElementFromEnd k xs
    | k < 0 = error "k must be non negative"
    | k == 0 = last xs
    | otherwise = kElementFromEnd (k-1) (init xs)

编写这样一个函数绝对是一个很好的练习。首先,GHC可以帮助您捕获这样的错误。特别是-如果您的模式不完整,fwarn complete patterns将发出警告,以防止出现严重的运行时错误。使用-Werror,您还可以将其设置为错误,以确保不会错过警告。使用-Wall-Werror编译以消除所有可能的警告并不是一个坏习惯

第二,使练习更难:由于init处于启用状态,因此函数的实现是ok,或者更准确地说是Omink,n。对于非常小的k,这并不重要,但是如果k≈n、 您将获得二次性能。因此,我建议您尝试找到一个

在独立于k的情况下, 适用于大型、惰性列表;也就是说,遍历一个列表的方式是,如果没有其他东西将该列表保存在内存中,则GC会不断地丢弃列表开头的元素;特别是,空间复杂度应该独立于n。 扰流板:

kElementFromEnd k xs=最后一个$zipWith const xs drop k xs

另外,通常最好是优雅地失败,因为调用错误意味着您的程序立即存在。因此,另一个改进是创建类型签名

kElementFromEnd :: Int -> [x] -> Maybe x

编写这样一个函数绝对是一个很好的练习。首先,GHC可以帮助您捕获这样的错误。特别是-如果您的模式不完整,fwarn complete patterns将发出警告,以防止出现严重的运行时错误。使用-Werror,您还可以将其设置为错误,以确保不会错过警告。使用-Wall-Werror编译以消除所有可能的警告并不是一个坏习惯

第二,使练习更难:由于init处于启用状态,因此函数的实现是ok,或者更准确地说是Omink,n。对于非常小的k,这并不重要,但是如果k≈n、 您将获得二次性能。因此,我建议您尝试找到一个

在独立于k的情况下, 适用于大型、惰性列表;也就是说,遍历一个列表的方式是,如果没有其他东西将该列表保存在内存中,则GC会不断地丢弃列表开头的元素;特别是,空间复杂度应该独立于n。 扰流板:

kElementFromEnd k xs=最后一个$zipWith const xs drop k xs

另外,通常最好是优雅地失败,因为调用错误意味着您的程序立即存在。因此,另一个改进是创建类型签名

kElementFromEnd :: Int -> [x] -> Maybe x

但我想看最后一个元素,因为x:xs的模式一直在将其拆分为头部和尾部。我要最后一个和头。我如何通知haskell我正在寻找一个多元素列表?谢谢!不过我一定是误解了什么。假设我在编写kElementFromEnd时没有首先声明它的签名第一行代码,haskell怎么能断定xs是一个列表呢?我是否应该使用[x]符号来表示它是函数体中的一个列表?@vondip[x]是一种类型,这里使用的x与函数参数的名称无关。该类型完全等同于Int->[a]->[a]或Int->[element]->[element]。当对参数进行模式匹配时,您实际上要做的是对数据类型的构造函数进行模式匹配。列表具有构造函数[]::[a]和:::a->[a]->[a]。Haskell还有特殊的语法sugar,用于处理[a,b,c,d]等列表文字,但编译器在内部将其转换为a:b:c:d:[]。因此,[x]==x:[].@vondip如果您在构造函数上不匹配,那么您只是在自己使用名称,比如addxy=x+y或take5xs=take5xs。这里使用的x、y和xs的名称与传递到那里的任何值都匹配,这是一个命名的catch all。您还可以使用ux语法匹配任何不想将该值绑定到名称的值,如const x ux=x。在本例中,Haskell可以确定它是一个列表,因为您正在对它调用last::[a]->a和init::[a]->[a]。Haskell很聪明,他知道xs只能是一个列表。@vondip Haskell怎么能断定xs是一个列表?嗯,一对
方法:它显然是第一个等式中的一个列表,因为你使用了模式[],它显然是第二个等式中的一个列表,因为你把它传递给last和init,后者只对列表有效。但是我想取最后的元素,因为x:xs的模式一直将它拆分为head和tail。我要最后一个和头。我如何通知haskell我正在寻找一个多元素列表?谢谢!不过我一定是误解了什么。假设我在编写kElementFromEnd时没有首先声明它的签名第一行代码,haskell怎么能断定xs是一个列表呢?我是否应该使用[x]符号来表示它是函数体中的一个列表?@vondip[x]是一种类型,这里使用的x与函数参数的名称无关。该类型完全等同于Int->[a]->[a]或Int->[element]->[element]。当对参数进行模式匹配时,您实际上要做的是对数据类型的构造函数进行模式匹配。列表具有构造函数[]::[a]和:::a->[a]->[a]。Haskell还有特殊的语法sugar,用于处理[a,b,c,d]等列表文字,但编译器在内部将其转换为a:b:c:d:[]。因此,[x]==x:[].@vondip如果您在构造函数上不匹配,那么您只是在自己使用名称,比如addxy=x+y或take5xs=take5xs。这里使用的x、y和xs的名称与传递到那里的任何值都匹配,这是一个命名的catch all。您还可以使用ux语法匹配任何不想将该值绑定到名称的值,如const x ux=x。在本例中,Haskell可以确定它是一个列表,因为您正在对它调用last::[a]->a和init::[a]->[a]。Haskell很聪明,他知道xs只能是一个列表。@vondip Haskell怎么能断定xs是一个列表?好的,有两种方法:第一个等式中的列表,因为你使用了模式[],第二个等式中的列表,因为你把它传递给last和init,后者只对列表起作用。你可以让这变得简单很多!kElemFE n xs=反向xs!!你可以让这一切简单得多!kElemFE n xs=反向xs!!N