如何将“紧凑”Python移植到“紧凑”Java?

时间:2012-09-27 22:41:07

标签: java python

一位朋友正在进行在线Scala课程并分享此内容。

# Write a recursive function that counts how many different ways you can make
# change for an amount, given a list of coin denominations. For example, there
# are 3 ways to give change for 4 if you have coins with denomiation 1 and 2:
# 1+1+1+1, 1+1+2, 2+2.

如果您正在参加并仍在研究解决方案,请不要阅读此内容!

(免责声明:即使我的Python解决方案可能有问题,我也不想影响你的想法,如果你在课程中,不管怎么样!我猜这是思考< / em>进入它会产生学习,而不仅仅是“解决”......)


除此之外......

我以为我会在Python中使用它,因为我没有Scala排版(我自己不在课程中,只是对学习Python和Java感兴趣并欢迎“练习”练习)。

这是我的解决方案,我希望尽可能使用紧凑的表示法移植到Java:


def show_change(money, coins, total, combo):
  if total == money:
    print combo, '=', money
    return 1
  if total > money or len(coins) == 0:
    return 0
  c = coins[0]
  return (show_change(money, coins, total + c, combo + [c]) +
    show_change(money, coins[1:], total, combo))

def make_change(money, coins):
  if money == 0 or len(coins) == 0:
    return 0
  return show_change(money, list(set(coins)), 0, [])

def main():
  print make_change(4, [2, 1])

if __name__ == '__main__':
  main()

问题

  • 如何在Java中使用JDK,如果它们有帮助,可以使用JDK外部的库吗?

我尝试自己进行移植但是它已经非常详细而且我认为通常“必须有更好的方法来做这个”

这是我的尝试:


import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
public class MakeChange {
  static int makeChange(int money, int[] coins) {
    if (money == 0 || coins.length == 0) {
      return 0;
    }
    return showChange(money, Ints.asList(coins), 0, new ArrayList<Integer>());
  }
  static int showChange(int money, List<Integer> coins, int total,
      List<Integer> combo) {
    if (total == money) {
      System.out.printf("%s = %d%n", combo, total);
      return 1;
    }
    if (total > money || coins.isEmpty()) {
      return 0;
    }
    int c = coins.get(0);
    List<Integer> comboWithC = Lists.newArrayList(combo);
    comboWithC.add(c);
    return (showChange(money, coins, total + c, comboWithC) + showChange(money,
        coins.subList(1, coins.size()), total, combo));
  }
  public static void main(String[] args) {
    System.out.println(makeChange(4, new int[] { 1, 2 }));
  }
}

具体来说,我不喜欢的是不得不做下面的事情只是为了传递一个附加了元素的列表副本:

    List<Integer> comboWithC = Lists.newArrayList(combo);
    comboWithC.add(c);

请告诉我Java的紧凑性和可读性。我仍然是两种语言的初学者......

1 个答案:

答案 0 :(得分:1)

实际上,你在这里所做的几乎所有事情都可以直接转换为Java,没有太多额外的冗长。

例如:

def make_change(money, coins):
  if money == 0 or len(coins) == 0: return 0
  return calculate_change(money, list(set(coins)), 0)

明显的Java等价物是:

public static int make_change(int money, int coins[]) {
  if (money == 0 || coins.length == 0) return 0;
  return calculate_change(money, coins, 0);
}

这里和那里有一些额外的单词,因为括号支撑而有额外的一行,当然还有明确的类型......但除此之外,没有太大的变化。

当然更多的Python和Javariffic(等同的单词?)版本将是:

def make_change(money, coins):
  if money == 0 or len(coins) == 0: 
    return 0
  return calculate_change(money, list(set(coins)), 0)

明显的Java等价物是:

public static int make_change(int money, int coins[]) {
  if (money == 0 || coins.length == 0) {
    return 0;
  }
  return calculate_change(money, coins, 0);
}

因此,Java获得了一个额外的结束括号加上几个空白字符;仍然没什么大不了的。

将整个事物放在一个类中,并将main转换为一个方法,增加了大约3行。初始化显式数组变量而不是使用[2, 1]作为文字是另外1。并且System.out.printlnprint长几个字符,lengthlen长3个字符,每个评论需要两个字符//而不是# {1}}。但我怀疑你担心的是什么。

最终,总共有一行很棘手:

return (calculate_change(money, coins, total + c, combo + [c]) +
    calculate_change(money, coins[1:], total, combo))

Java int coins[]没有任何方法可以说“给我一个带有当前尾部的新数组”。最简单的解决方案是传递额外的start参数,所以:

public static int calculate_change(int money, int coins[], int start, int total) {
    if (total == money) {
        return 1;
    }
    if (total > money || coins.length == start) {
        return 0;
    }
    return calculate_change(money, coins, 0, total + coins[start]) +
        calculate_change(money, coins, start + 1 total);
}

事实上,几乎所有东西都可以简单地转换为C;你只需要为数组的长度传递另一个参数,因为你不能像在Java或Python中那样在运行时计算它。

你抱怨的那一行是一个有趣的观点,值得多考虑一下。在Python中,你已经(实际上):

comboWithC = combo + [c]

使用Java的List,这是:

List<Integer> comboWithC = Lists.newArrayList(combo);
comboWithC.add(c);

更详细。但那是故意的。 Java List<>并不意味着以这种方式使用。对于小型列表,复制一切并不是什么大问题,但对于大型列表,它可能会造成巨大的性能损失。 Python的list是围绕这样的假设而设计的,即大多数情况下,你处理小型列表,并且复制它们是完全正常的,所以编写它应该是微不足道的。 Java的List是基于这样的假设而设计的:有时候,你正在处理大型列表,并且复制它们是一个非常糟糕的主意,因此你的代码应该清楚地表明你真的想要这样做。

理想的解决方案是使用不需要复制列表的算法,或者找到旨在以这种方式复制的数据结构。例如,在Lisp或Haskell中,默认列表类型对于这种算法是完美的,并且有大约69105个用于“Java中的Lisp样式列表”或“Java缺点”的配方,您应该能够在线找到它们。 (当然你也可以在List上编写一个简单的包装器,它添加了一个像Python __add__这样的“addToCopy”方法,但这可能不是正确的答案;你想编写惯用的Java,或者为什么要使用Java代替许多其他JVM语言之一?)