【发布时间】:2021-04-03 16:31:36
【问题描述】:
我决定自学 Haskell,并尝试将一些代码从 Java 翻译成 Haskell,这样我就可以更熟悉递归、回溯和搜索树修剪。
Java 代码:
private static boolean isListOkay(ArrayList<Integer> numbers) {
return listSum(numbers) == 8 && numbers.size() == 3;
}
private static int listSum(ArrayList<Integer> numbers) {
int sum = 0;
for (Integer number : numbers)
sum += number;
return sum;
}
public static ArrayList<Integer> sumTo8(ArrayList<Integer> numbers) {
return sumTo8(numbers, 0, new ArrayList<Integer>());
}
private static ArrayList<Integer> sumTo8(ArrayList<Integer> numbers, int i, ArrayList<Integer> list) {
if (isListOkay(list))
return list;
else if (i == numbers.size() && !isListOkay(list))
return null;
else if (listSum(list) > 8 || listSum(list) == 8 && list.size() != 3)
return null;
else {
int currentNumber = numbers.get(i);
ArrayList<Integer> pickIt = new ArrayList<>(list);
pickIt.add(currentNumber);
ArrayList<Integer> leaveIt = new ArrayList<>(list);
ArrayList<Integer> pickItResult = sumTo8(numbers, i + 1, pickIt);
if (pickItResult == null)
return sumTo8(numbers, i + 1, leaveIt);
return pickItResult;
}
}
Haskell 代码:
listSumUtil :: [Int] -> Int -> Int
listSumUtil [] sum = sum
listSumUtil (x:xs) sum = x + y
where y = listSumUtil xs sum
listSum :: [Int] -> Int
listSum list = listSumUtil list 0
sumTo8Util :: [Int] -> [Int] -> [Int]
sumTo8Util [] list
| sum == 8 && listLength == 3 = list
| otherwise = []
where sum = listSum list
listLength = length list
sumTo8Util (x:xs) l2 =
if sum > 8 && listLength > 3 then []
else if sum == 8 && listLength == 3 then l2
else (if l3 == [] then l4 else l3)
where sum = listSum l2
listLength = length l2
l3 = sumTo8Util xs pickIt
pickIt = l2 ++ [x]
l4 = sumTo8Util (x:xs) l2
sumTo8 :: [Int] -> [Int]
sumTo8 list = sumTo8Util list []
Java 代码正在运行,我能够编译 Haskell 代码。当我执行 main 时,虽然没有输出并且它一直在运行,所以某处必须有一个无限循环,这就是我需要你帮助的地方。如何在 Haskell 中实现准确的 Java 代码?我在实施中遗漏了什么吗?如您所见,我在 Haskell 代码中避免了语法糖,因为我刚刚开始并且还不能理解它。
更新 1:
在 Haskell 中添加 else if sum == 8 && listLength == 3 then l2 条件 代码,但仍然不起作用。
更新 2:
找到了一种方法。
工作代码:
listSum :: [Int] -> Int
listSum list = foldl (+) 0 list
insertAtEnd :: [Int] -> Int -> [Int]
insertAtEnd [] c = [c]
insertAtEnd (h:t) c = h : insertAtEnd t c
sumTo8Util :: [Int] -> Int -> [Int] -> [Int]
sumTo8Util lst i rlst
| (length rlst == 3) && (listSum rlst == 8) = rlst
| (i == length lst) && ((listSum rlst /= 8) || (length rlst /= 3)) = []
| otherwise = if (length pickIt == 0) then (sumTo8Util lst (i+1) rlst) else pickIt
where number = lst !! i
nrlst = insertAtEnd rlst number
pickIt = sumTo8Util lst (i+1) nrlst
sumTo8 :: [Int] -> [Int]
sumTo8 list = sumTo8Util list 0 []
基本上我尝试通过返回空列表来触发回溯。
如果有替代方案可以使用回溯并且比我的代码更有效*,请随时提出建议。
*肯定会像我这几天自学 Haskell 一样
【问题讨论】:
-
"如何在 Haskell 中实现准确的 Java 代码?"一个不会。它们是完全不同的语言,需要完全不同的心态。将代码“翻译”成另一种语言通常会在目标语言中产生错误/非惯用代码。从 OOP 转换为函数式编程会给你各种糟糕的代码。不要这样做。从教程开始学习 Haskell。
-
作为一个例子来说明你的代码有多不习惯:你的
ListSum已经被提供为Prelude.sum = foldr (+) 0——这是一个非正式的定义,但优化/通用化的定义仍然是一行代码。该定义不需要模仿程序语言对累加器的破坏性分配。如果你研究一下 Haskell 的工作原理,你会更有效地学习 Haskell。
标签: haskell recursion functional-programming backtracking code-translation