[英]Translating a piece of Java code to Haskell
我正在解决Baby Blocks问题。 我有一些java代码,我想将其转换为Haskell:
Java的:
for (int i = 1; i <optHeight.length ; i++) {
int maxHeightIndex = 0;
for (int j = i-1; j >=0 ; j--) {
// Need help from here
if(boxes[j].width>boxes[i-1].width && boxes[j].depth>boxes[i-1].depth) {
if(optHeight[maxHeightIndex]<optHeight[j+1]) { <-- How do I write this condition
maxHeightIndex = j+1;
}
}
}
optHeight[i]=optHeight[maxHeightIndex] + boxes[i-1].height;
}
其中optHeight
是一维数组,而boxes
是一个以height, width, depth
为数据成员的对象。 在Haskell中,它只是一个列表列表。 由于缺少可变数组/变量,我完全无能为力。
哈斯克尔:
b list = do
forM_ [1..length list] $ \i -> do
let maxHeight = 0
forM_ [0..(i-1)] $ \j -> do
if list!!j!!1 > list!!i-1!!1 && list!!j!!2 > list !!j!!2 then
maxHeight = j + 1
PS:我完全是Haskell的初学者
通过组合非常简单的函数来找到整体解决方案,可以以非常易读的方式解决这个问题。 可能的策略如下:
相应的代码如下(以下说明):
type Block = (Int,Int,Int)
type Tower = [Block]
babyBlocks :: [Block] -> Int
babyBlocks blocks = highest $ converge allBlocks initialTowers
where allBlocks = possibleBlocks blocks
initialTowers = map (:[]) allBlocks
possibleBlocks :: [Block] -> [Block]
possibleBlocks = concatMap (\(w,d,h) -> [(w,d,h),(w,h,d),(d,h,w)])
canStack :: Block -> Block -> Bool
canStack (w1,d1,_) (w2,d2,_) = w2 < w1 && d2 < d1 || w2 < d1 && d2 < w1
expand :: Tower -> [Block] -> [Tower]
expand tower@(top:_) = map (:tower) . filter (canStack top)
converge :: [Block] -> [Tower] -> [Tower]
converge blocks towers | null newTowers = towers
| otherwise = converge blocks newTowers
where newTowers = concatMap (flip expand blocks) towers
height :: Tower -> Int
height = sum . map (\(_,_,h) -> h)
highest :: [Tower] -> Int
highest = maximum . map height
babyBlocks
:此函数为给定的块类型生成所有可能的块(通过旋转它们),将它们转换为初始塔组(通过将它们包装成带有一个元素的简单列表)并开始将初始塔组汇聚到最终塔中组。 possibleBlocks
:对于给定的块类型集,此函数通过旋转它们返回所有可能的块。 严格来说,应该有3! = 6
3! = 6
旋转(三个坐标的所有排列),但我们只需要考虑其中的一半,因为我们可以将交换宽度和深度的旋转视为重复。 canStack
:检查给定块是否可以放在另一个块上。 expand
:对于给定的塔,该功能会检查所有可用的块,如果它们可以放在塔的顶部。 为每个可以放在顶部的兼容块创建一个新塔。 converge
:这个函数基本上重复了一组塔的expand
,直到其中一个塔不再有块。 解决方案必须是剩余塔的最大高度。 height
:通过总计其块高来返回给定塔的高度。 highest
:对于给定的塔组,标识最大高度。 解决这个问题的方法(我认为)是考虑每个盒子的每次旋转(所以你有3n
总旋转)。 然后,您根据其基数的增加来订购这些。 那么问题就是选择彼此“适合”的盒子的最长子序列(你不必担心两次选择相同的盒子,因为盒子永远不适合自己)。 这听起来很像规范最长的子序列问题,这表明我们需要一个动态的编程解决方案。 我们将有一个长度为3n
的数组,其中第i个元素表示您可以使用顶部的第i个框创建的堆栈的最大高度。
maxHeight(i) = { height(i) + max[ maxHeight(j) ] such that
width(j) > width(i), depth(j) > depth(i), 0 <= j < i }
现在,让我们开始使用Haskell解决方案。 我假设您的输入是维度列表。 请注意代码与我描述的解决方案有多接近 - 诀窍是以声明方式编写代码。
import Data.List (sortOn)
import Data.Vector (fromList, (!), generate)
import Data.Ord (Down(..))
babyBlocks :: [(Int,Int,Int)] -> Int
babyBlocks blocks = maxHeights ! (3*n - 1)
where
-- get the number of blocks
n = length blocks
-- generate the `3n` blocks formed by rotating the existing blocks,
-- sort them by their base size, and store them in a vector for
-- fast retrieval
sortedBlocks = fromList
. sortOn (\(x,y,z) -> Down (x*y))
. concatMap (\(x,y,z) -> [(x,y,z),(y,z,x),(z,x,y)])
$ blocks
-- we'll make ourselves a couple helper functions, just so
-- our translation of the recurrence relation looks better
height n = let (_,_,z) = sortedBlocks ! n in z
width n = let (_,y,_) = sortedBlocks ! n in y
depth n = let (x,_,_) = sortedBlocks ! n in x
maxHeight n = maxHeights ! n
-- state the dynamic programming
maxHeights = generate (3*n) (\i ->
height i + maximum (0 : [ maxHeight j | j<-[0..(i-1)]
, width j > width i
, depth j > depth i ]))
您似乎遇到麻烦的部分是最后一部分的动态编程。 因为Haskell是懒惰的,所以在定义maxHeights
使用maxHeight
实际上是完全可以的,即使我不知道我的向量将被初始化的顺序是什么!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.