Haskell - ForM循环后值不保存

时间:2018-04-09 18:46:48

标签: haskell functional-programming

我希望能够在haskell中创建一个程序,它可以从一个数字列表中找到一个不断增加的子序列(尚未完成,这部分是为每个子列表计算最长的子序列在该子列表中的内容)。该程序基本上取自诸如

之类的输入
1
5
1 2 9 6 8

其中第一行是测试用例的数量,第二行是特定测试用例中的数字,第三行是测试用例本身。它正在寻找测试用例中的多个增加序列。以下是我到目前为止的情况:

main = do   
    inputCases <- getLine
    let cases = (read inputCases :: Int)
    forM [1..cases] $ \num -> do
        inputNumbers <- getLine
        let numbers = (read inputNumbers :: Int)  
        something <- getLine
        let singlewords = words something
            list = f singlewords
        let hello = grid numbers numbers 0
        let second  = hello
        print list
        forM [0..numbers] $ \check -> do
            forM [check..numbers] $ \check2 -> do
                let val = 1
                let keeper = val
                forM [check..check2] $ \check3 -> do
                    let val = getVal hello list keeper check check2 check3
                    let keeper = val
                    return()
                print check
                print check2
                print val
                let hello = updateMatrix second val (check, check2)
                let second = hello


f :: [String] -> [Int]
f = map read

grid :: Int -> Int -> a -> [[a]]
grid x y = replicate y . replicate x

getVal :: [[Int]] -> [Int] -> Int -> Int -> Int -> Int -> Int
getVal m b second x y z = 
    if b!!z < b!!y && 1+m!!x!!z > second then 1+m!!x!!z
    else second

updateMatrix :: [[a]] -> a -> (Int, Int) -> [[a]]
updateMatrix m x (r,c) =
  take r m ++
  [take c (m !! r) ++ [x] ++ drop (c + 1) (m !! r)] ++
  drop (r + 1) m

然而,我的问题是,一旦程序退出ForM循环,它就不会保存变量&#34; hello&#34;或者在for循环中声明的任何内容。有没有更好的方法呢?递归会在这种情况下起作用吗?我不太确定如何实施 lis [i] [j]将保持{a [i],...,a [j]}

中增长最长的子序列的长度

这是我要翻译的python代码。鉴于此代码,除了我目前正在尝试的方式之外,还有更好的方法吗?

T = int(input())
for t in range(0, T):

    n = int(input())
    a = list(map(int, input().split()))

    lis = [[0 for j in range(0, n)] for i in range(0, n)]

    for i in range(0, n):
        for j in range(i, n):
            val = 1
            for k in range(i, j):
                if(a[k] < a[j] and 1 + lis[i][k] > val):
                    val = 1 + lis[i][k]
            lis[i][j] = val

2 个答案:

答案 0 :(得分:3)

在我的另一个答案中,我讨论了您在forM循环中稍后如何存储信息以便检索的问题的答案。在这个答案中,我将讨论来自其他语言的for循环的惯用语翻译;通常这是在Haskell中生成forM循环。

因为这是一个很好的编程练习,我不想放弃答案 - 自己解决问题有很多快乐和学习。但我确实想说明一种替代方法。为了保留我用Python代码编写的翻译的所有有趣内容,我将以稍微程式化的方式解决一个稍微容易的问题:而不是lis[i][j]给出索引之间最长的增长子序列的长度{{1在原始列表中,我们将i给出原始列表中索引jlis[i][j]之间的最大值。

这个想法将是这样的:我们不是迭代索引ij,而是迭代从i开始的后缀,然后是从{{j开始的后缀前缀。 1}}并以i结束。首先,我们将完成在每个中缀表达式上调用i的天真的事情。所以:

j

例如,我们可以在ghci中的示例列表中尝试:

maximum

请注意,这里的形状存在差异:在Python中我们生成了一个正方形结果,这里我们生成一个三角形结果,省略了与原始列表的实际中缀块不对应的无用条目。 (如果由于某种原因确实需要平方结果,则很容易重新引入虚拟值。)

这已经很不错了,非常惯用;但是,有一部分Python代码还没有很好地捕获:Python代码重用以前计算的值来进行一些动态编程。这也可以用于上面的代码,虽然它确实需要一些心理体操,你看到它的前几次。我们将使用惰性和递归来计算以后的结果。

这里的想法是在我们遍历后缀时保持滚动最大值,合并为我们在后缀的最大值列表中使用我们在后缀中看到的新值。所以:

import Data.List

maxes0 a =
    [ [ maximum inf
      | inf <- tail (inits suff)
      ]
    | suff <- init (tails a)
    ]

我们可以在ghci中看到它的作用是一样的:

> maxes0 [1,2,9,6,8]
[[1,2,9,9,9],[2,9,9,9],[9,9,9],[6,8],[8]]

你可以结合这两个想法(通过懒惰+递归使已经计算的位可用,并通过嵌套列表推导使中缀列表可用)来产生完全纯粹的Python代码的惯用翻译,没有提到在任何地方列出索引,并且不使用maxes1 a = [ let row = head suff : zipWith max row (tail suff) in row | suff <- init (tails a) ]

答案 1 :(得分:1)

forM返回一个值列表,每个输入元素在它所递送的列表中各一个,以及您在forM函数体中计算的任何值。因此,您可以使用通常的do - 符号绑定语法从循环体中提取信息。这是一个简单的例子,询问用户是否要加倍列表中的每个数字:

import Control.Monad
vals = [1..5]
main = do
    vals' <- forM vals $ \val -> do
        v <- getLine
        return (if v == "yes" then val*2 else val)
    print vals'

运行它的一个例子:

> main
yes
yes
no
no
yes
[2,4,3,4,10]

尽管此示例为简单起见返回了数字,但您可以通过这种方式从每次循环迭代中返回任意感兴趣的信息。