简体   繁体   English

Haskell中没有“长度”函数的列表的长度

[英]The length of a list without the "length" function in Haskell

I want to see how long a list is, but without using the function length .我想查看列表的长度,但不使用函数length I wrote this program and it does not work.我写了这个程序,但它不起作用。 Maybe you can tell me why?也许你能告诉我为什么? Thanks!谢谢!

let y = 0
main = do
  list (x:xs) = list (xs)
  y++

list :: [Integer] -> Integer
list [] = y

Your program looks quite " imperative ": you define a variable y , and then somehow write a do , that calls (?) the list function (?) that automagically seems to "return y " and then you want to increment y . 您的程序看起来非常“ 必要 ”:您定义了变量y ,然后以某种方式编写了一个do ,它调用(?) list函数(?),该函数似乎自动“返回y ”,然后您想要递增y

That's not how Haskell (and most functional and declarative) languages work: 那不是Haskell(以及大多数功能性和声明性)语言的工作方式:

  • in a declarative language, you define a variable only once, after the value is set, there is usually no way to alter its value, 在声明性语言中,仅定义一次变量 ,设置值后,通常无法更改其值,
  • in Haskell a do usually is used for monads, whereas the length is a pure function, 在Haskell中, do通常用于monad,而length是一个函数,
  • the let is a syntax construction to define a variable within the scope of an expression, let是用于在表达式范围内定义变量的语法构造,
  • ... ...

In order to program Haskell (or any functional language), you need to "think functional": think how you would solve the problem in a mathematical way using only functions . 为了对Haskell(或任何功能性语言)进行编程,您需要“考虑功能性”:思考如何使用函数数学方式解决问题。

In mathematics, you would say that the empty list [] clearly has length 0 . 在数学中,您会说空列表[]长度显然为0 Furthermore in case the list is not empty, there is a first element (the "head") and remaining elements (the "tail"). 此外,如果列表不为空,则存在第一个元素(“头”)和其余元素(“尾”)。 In that case the result is one plus the length of the tail. 在这种情况下,结果为一加尾的长度。 We can convert that in a mathematical expression, like: 我们可以将其转换为数学表达式,例如:

胶乳

Now we can easily translate that function into the following Haskell code: 现在,我们可以轻松地将该函数转换为以下Haskell代码:

ownLength :: [a] -> Int
ownLength [] = 0
ownLength (_:xs) = 1 + ownLength xs

Now in Haskell, one usually also uses accumulators in order to perform tail recursion : you pass a parameter through the recursive calls and each time you update the variable. 现在在Haskell中,通常还使用累加器来执行尾部递归 :您通过递归调用传递参数,并且每次更新变量时都会传递该参数。 When you reach the end of your recursion, you return - sometimes after some post-processing - the accumulator. 递归结束时,有时会经过一些后处理后返回累加器。

In this case the accumulator would be the so far seen length, so you could write: 在这种情况下,累加器将是迄今为止看到的长度,因此您可以编写:

ownLength :: [a] -> Int
ownLength = ownLength' 0
    where ownLength' a [] = a
          ownLength' a (_:xs) = ownLength' (a+1) xs

It looks you still think in an imperative way (not the functional way). 看起来您仍然以命令式的方式(而不是功能性的方式)思考。 For example: 例如:

  • you try to change the value of a "variable" (ie y++ ) 您尝试更改“变量”(即y++ )的值
  • you try to use "global variable" (ie y ) in the body of the list function 您尝试在list函数的主体中使用“全局变量”(即y

Here is the possible solution to your problem: 这是您的问题的可能解决方案:

main = print $ my_length [1..10]

my_length :: [Integer] -> Integer
my_length [] = 0
my_length (_:xs) = 1 + my_length xs

You can also run this code here: http://ideone.com/mjUwL9 . 您也可以在以下位置运行此代码: http : //ideone.com/mjUwL9

Please also note that there is no need to require that your list consists of Integer values. 另请注意,无需要求您的列表包含Integer数值。 In fact, you can create much more "agnostic" version of your function by using the following declaration: 实际上,可以通过使用以下声明来创建函数的更多“不可知论”版本:

my_length :: [a] -> Integer

Implementation of this function doesn't rely on the type of items from the list, thus you can use it for a list of any type. 此功能的实现不依赖于列表中项目的类型,因此您可以将其用于任何类型的列表。 In contrast, you couldn't be that much liberal for, for example, my_sum function (a potential function that calculates the sum of elements from the given list). 相反,例如my_sum函数(一个可能的函数可以计算给定列表中元素的总和)就不能那么自由。 In this situation, you should define that your list consists of some numerical type items. 在这种情况下,您应该定义列表由一些数字类型项组成。

At the end, I'd like to suggest you a fantastic book about Haskell programming: http://learnyouahaskell.com/chapters . 最后,我想向您推荐一本关于Haskell编程的出色书籍: http : //learnyouahaskell.com/chapters

Other answers have already beautifully explained the proper functional approach. 其他答案已经很好地解释了正确的功能方法。 It looks like an overkill but here is another way of implementing the length function by using only available higher order functions. 看起来有些矫kill过正,但这是通过仅使用可用的高阶函数来实现length函数的另一种方法。

my_length :: [a] -> Integer
my_length = foldr (flip $ const . (+1)) 0

I've found this solution in Learn you a haskell .我在Learn you a haskell 中找到了这个解决方案。

length' xs = sum [1 | _ <- xs]

It replaces every element of the list with 1 and sums it up.它将列表中的每个元素替换为 1 并将其求和。

Probably the simplest way is to convert all elements to 1 and then to sum the new elements: 可能最简单的方法是将所有元素转换为1 ,然后对新元素求和:

sum . map (const 1)

For added speed: 为了提高速度:

foldl' (+) 0 . map (const 1)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM