将问题拆分为两个更简单的子问题的简单方法包括:
- 获取最右边最大值的位置索引
- 编写一个通用函数
del,用于消除列表中给定位置的元素。这不需要Ord 约束。
如果我们被允许使用常规库函数,ved 可以这样写:
ved0 :: Ord a => [a] -> [a]
ved0 [] = []
ved0 (x:xs) =
let
(maxVal,maxPos) = maximum (zip (x:xs) [0..])
del k ys = let (ys0,ys1) = splitAt k ys in (ys0 ++ tail ys1)
in
del maxPos (x:xs)
zip 生成的对按字典顺序排序,从而确保选择最右边的最大值。
我们需要手动递归替换库函数。
关于第 1 步,即找到最右边的最大值的位置,正如通常所做的那样,我们可以使用递归步进函数及其上方的包装器。
递归步进函数将整个计算上下文作为参数,即:
- 最大值的当前候选者,
mxv
- 当前最大值的最右边位置,
mxp
- 当前进入原始列表的深度,
d
- 原始列表的其余部分,
xs
它返回一对:(currentMaxValue, currentMaxPos)
-- recursive stepping function:
findMax :: Ord a => a -> Int -> Int -> [a] -> (a, Int)
findMax mxv mxp d [] = (mxv,mxp)
findMax mxv mxp d (x:xs) = if (x >= mxv) then (findMax x d (d+1) xs)
else (findMax mxv mxp (d+1) xs)
-- top wrapper:
lastMaxPos :: Ord a => [a] -> Int
lastMaxPos [] = (-1)
lastMaxPos (x:xs) = snd (findMax x 0 1 xs)
第 2 步,删除位置 k 的列表元素,可以以非常相似的方式处理:
-- recursive stepping function:
del1 :: Int -> Int -> [a] -> [a]
del1 k d [] = []
del1 k d (x:xs) = if (d==k) then xs else x : del1 k (d+1) xs
-- top wrapper:
del :: Int -> [a] -> [a]
del k xs = del1 k 0 xs
把它们放在一起:
我们现在可以编写最终的基于递归的ved 版本。为简单起见,我们将包装函数的内容内联而不是调用它们。
-- ensure we're only using authorized functionality:
{-# LANGUAGE NoImplicitPrelude #-}
import Prelude (Ord, Eq, (==), (>=), (+), ($), head, tail,
IO, putStrLn, show, (++)) -- for testing only
ved :: Ord a => [a] -> [a]
ved [] = []
ved (x:xs) =
let
findMax mxv mxp d [] = (mxv,mxp)
findMax mxv mxp d (y:ys) = if (y >= mxv) then (findMax y d (d+1) ys)
else (findMax mxv mxp (d+1) ys)
(maxVal,maxPos) = findMax x 0 1 xs
del1 k d (y:ys) = if (d==k) then ys else y : del1 k (d+1) ys
del1 k d [] = []
in
del1 maxPos 0 (x:xs)
main :: IO ()
main = do
let xs = [1,2,3,7,3,2,1,7,3,5,7,5,4,3]
res = ved xs
putStrLn $ "input=" ++ (show xs) ++ "\n" ++ " res=" ++ (show res)