多参数函数记忆化问题求助:(Int, Int) -> [[Int]] -> Int函数调试
解决多参数函数的记忆化问题
首先咱们先拆解你的代码里的几个关键问题:
- 把整个矩阵作为记忆化键的一部分:
[[Int]]是复杂的嵌套列表,既不能作为数组的索引类型(Data.Array的索引需要是可枚举、可比较的简单类型),也会导致记忆化完全失效——每次传入不同的矩阵(哪怕内容一样)都会被当成新键,而且数组的边界定义((1,1), [[1,1]]), ((n,n), [[n,n]])完全不符合数组索引的要求,这会直接导致数组初始化失败。 - 每次调用都会重新创建memo:你的
memo定义在function的where块里,意味着每次调用function都会生成一个全新的数组,根本没法复用之前计算的结果。 - n的来源未定义:代码里的
n没有明确赋值,应该是矩阵的边长(比如length matrix),但你没关联起来。
正确的实现思路:柯里化+闭包捕获矩阵
我们可以把函数重构为柯里化形式:先接收矩阵参数,返回一个针对(Int, Int)的记忆化函数。这样矩阵会被闭包捕获,记忆化的键只需要(Int, Int)这个简单的元组,完全符合数组索引的要求。
具体实现代码
import Data.Array (Array, listArray, (!), range) -- 先柯里化:接收矩阵,返回一个记忆化的(Int, Int) -> Int函数 function :: [[Int]] -> (Int, Int) -> Int function matrix = memoizedInner where n = length matrix -- 假设矩阵是n×n的,可根据你的实际逻辑调整 -- 定义记忆化数组的边界:(i,k)的范围是1<=i<=n,0<=k<=n bounds = ((1, 0), (n, n)) -- 记忆化数组:预计算所有(i,k)对应的结果 memo :: Array (Int, Int) Int memo = listArray bounds [inner i k | (i,k) <- range bounds] -- 核心递归逻辑 inner i 0 = matrix !! (i-1) !! 0 -- 保持你原代码的索引转换逻辑 inner i k | i == 1 = memo ! (i, k-1) -- 处理i=1的边界,避免i-1=0越界 | otherwise = max (memo ! (i, k-1)) (memo ! (i-1, k)) -- 对外暴露的记忆化函数 memoizedInner (i,k) = memo ! (i,k)
关键改进点解释
- 柯里化重构:把
(Int, Int) -> [[Int]] -> Int改成[[Int]] -> (Int, Int) -> Int,这样当你传入一个矩阵后,会得到一个专门针对该矩阵的记忆化函数,后续调用这个函数时,所有(i,k)的结果都会从缓存里取。 - 简化记忆化键:只把
(Int, Int)作为索引,矩阵被闭包捕获,不需要作为键的一部分——因为同一个矩阵对应的(i,k)结果是固定的,这样记忆化才有意义。 - 正确的数组边界:根据矩阵的边长
n定义(i,k)的合法范围,确保数组能正确初始化,不会出现索引越界。 - 边界处理:增加了
i==1的判断,避免递归时出现i-1=0的非法索引(如果你的原逻辑允许i从1开始的话)。
使用示例
-- 测试用的3×3矩阵 testMatrix = [[1,2,3], [4,5,6], [7,8,9]] -- 获取针对该矩阵的记忆化函数 calc = function testMatrix -- 调用计算 main = do print $ calc (3,2) -- 会返回max(calc(3,1), calc(2,2)),最终结果是9
如果你坚持原函数的参数顺序
如果你不想改变原函数的参数顺序(先传(s,d)再传矩阵),可以用一个辅助函数来包装:
function :: (Int, Int) -> [[Int]] -> Int function (s,d) matrix = functionCurried matrix (s,d) where functionCurried = -- 这里放上面的柯里化实现代码
这样既保留了原函数的参数顺序,又能实现正确的记忆化。
内容的提问来源于stack exchange,提问作者WhiteW
相关产品推荐
相关产品推荐

