如何将“紧凑” Python移植为“紧凑” Java? - java

一位朋友正在在线进行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解决方案可能是错误的,我也不想影响您的思维方式(如果您正在学习一种或另一种方式的学习方式!) “解决” ...)

那边...

我以为我可以在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外部的库吗?

我尝试自己进行移植,但是它变得非常冗长,我认为通常的“一定有更好的方法”!

这是我的尝试:

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的紧凑性和可读性。我还是这两种语言的初学者。

参考方案

确实,您在这里所做的几乎所有操作都可以直接转换为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个字符,并且每个注释使用两个字符//而不是一个#。但我怀疑这是您所担心的。

最终,总共只有一条线很棘手:

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<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语言?)

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…

Java-如何将此字符串转换为日期? - java

我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…

Java:从类中查找项目名称 - java

仅通过类的实例,如何使用Java反射或类似方法查找项目名称?如果不是,项目名称(我真正想要的是)可以找到程序包名称吗? 参考方案 项目只是IDE使用的简单组织工具,因此项目名称不是类或JVM中包含的信息。要获取软件包,请使用Class#getPackage()。然后,可以调用Package#getName()将包作为您在代码的包声明中看到的String来获取…