整数列表中最长子序列的长度

时间:2016-04-30 20:09:46

标签: list haskell functional-programming

我想要擅长功能编程,所以我为自己设定了一些任务。

我想确定整数列表中最长子序列的长度,其中下一个元素 增加1 。< / p>

所以结果应该是

incsubseq [] ~?= 0,
incsubseq [5] ~?= 1,
incsubseq [1,2,3,5,6] ~?= 3,
incsubseq [5,6,1,2,3] ~?= 3,
incsubseq [5,6,1,4,3] ~?= 2,
incsubseq [6,5,4,3,2,1] ~?= 1]

我的尝试是这样的:

incsubseq :: [Int] -> Int
incsubseq [] = 0
incsubseq [_] = 1
incsubseq (a:b)
          | a == ((head b)-1) = 1 + (incsubseq b)
          | a /= ((head b)-1) = (incsubseq b)

但当然这仅适用于没有较长子序列的列表,例如[ 1,2,3 ,42] = 3 ,但不适用于[1,2, 100,101,102 ]这样的列表,应该是3但是是(它是2)!

我真的非常感谢你的帮助,因为这个问题让我疯狂,来自OO-编程。

3 个答案:

答案 0 :(得分:4)

你在同一时间解决了太多问题 - 我会尝试以更易懂的步骤解决问题

  1. 在列表中创建所有序列
  2. 使用length
  3. 创建一个包含map的新列表
  4. 找到maximum长度
  5. 如果您的序列是&#34;所有相同的事情&#34;现在第一部分会很容易。那么来自group的{​​{1}}就足够了,但在这里情况并非如此,不幸的是Data.List这正是你所寻找的 - 不起作用(由于技术原因我做的不想扩大。)

    首先,您需要实现自己的groupBy (\x y -> x + 1 == y)功能或&#34;作弊&#34;并查看here我得到的地方

      

    groupBy'

    然后你可以简单地 groupBy :: (a -> a -> Bool) -> [a] -> [[a]] groupBy rel [] = [] groupBy rel (x:xs) = (x:ys) : groupBy rel zs where (ys,zs) = groupByAux x xs groupByAux x0 (x:xs) | rel x0 x = (x:ys, zs) where (ys,zs) = groupByAux x xs groupByAux y xs = ([], xs)

    接下来的步骤应该是可以管理的。

    注意:如果您想要最长的序列,可以创建一个快捷方式并使用groupBy (\x y -> x + 1 == y) [1,2,100,101,102]

    完整解决方案:

      

    maximumBy (compare `on` length)

答案 1 :(得分:3)

您可以使用名为 accumulators 的技术解决此问题:您可以将某个函数与沿运行时修改的某些值一起调用。然后,基本情况返回使用这些 accumulators 计算的函数的结果。

对于这个特定问题,我们引入了两个累加器:curimaxicuri存储当前序列的长度,maxi包含迄今为止看到的最大递增序列。

基本情况是当我们到达列表的末尾时,我们返回maxi

maxincsub [] curi maxi = maxi

另一个基本情况是我们遇到最后一个元素。在这种情况下,我们会返回maxicuri的最大值:

maxincsub [_] curi maxi = max curi maxi

归纳案例是当我们在列表中看到两个元素时:我们确定a1是否为a2的增量。如果是,我们递增curi。如果没有,我们会将curi设置回1,但首先将maxi设置为此maxi的最大值curi

maxincsub (a0:a1:as) curi maxi | a0+1 == a1 = maxincsub (a1:as) (curi+1) maxi
                               | otherwise = maxincsub (a1:as) 1 (max curi maxi)

或将它们放在一起:

maxincsub [] curi maxi = maxi
maxincsub [_] curi maxi = max curi maxi
maxincsub (a0:a1:as) curi maxi | a0+1 == a1 = maxincsub (a1:as) (curi+1) maxi
                               | otherwise = maxincsub (a1:as) 1 (max curi maxi)

最后,我们需要通过设置incsubseqmaxincsub的初始值来将您的函数curi与我们的maxi绑定:

incsubseq :: [Int] -> Int
incsubseq xs = maxincsub xs 1 0

用你给定的输入来判断:

*Main> incsubseq []
0
*Main> incsubseq [5]
1
*Main> incsubseq [1,2,3,4,5,6]
6
*Main> incsubseq [1,2,3,5,6]
3
*Main> incsubseq [5,6,1,2,3]
3
*Main> incsubseq [5,6,1,4,3]
2
*Main> incsubseq [6,5,4,3,2,1]
1

答案 2 :(得分:0)

您可以将折叠函数用于此类递归函数。

这可能不是惯用的,但这是我使用foldl的版本。

incsubseq [] = 0
incsubseq (x:xs) = (\(a,_,_)->a) $ foldl update (1,1,x) xs
    where
        update (maxn,n,h) x
            | h+1 /= x      = (maxn, 1, x)
            | n+1 > maxn    = (n+1, n+1, x)
            | otherwise     = (maxn, n+1, x)