2011-08-02 31 views
3

通过举例的方式考虑下面简单的Python功能:如何将这个duck-typing(Python)转换为Java泛型?

def quantize(data, nlevels, quantizer=lambda x, d: int(floor(x/d))): 
    llim = min(data) 
    delta = (max(data) - llim)/(nlevels - 1) # last level x == max(data) only 
    y = type(data) 
    if delta == 0: 
     return y([0] * len(data)) 
    else: 
     return y([quantizer(x - llim, delta) for x in data]) 

这里,它是在行动:

>>> from random import random 
>>> data = [10*random() for _ in range(10)] 
>>> data 
[6.6181668777075018, 9.0511321773967737, 1.8967672216187881, 7.3396890304913951, 
4.0566699095012835, 2.3589022034131069, 0.76888247730320769, 8.994874996737197, 
7.1717500363578246, 2.887112256757157] 
>>> quantize(data, nlevels=5) 
[2, 4, 0, 3, 1, 0, 0, 3, 3, 1] 
>>> quantize(tuple(data), nlevels=5) 
(2, 4, 0, 3, 1, 0, 0, 3, 3, 1) 
>>> from math import floor 
>>> quantize(data, nlevels=5, quantizer=lambda x, d: (floor(x/d) + 0.5)) 
[2.5, 4.5, 0.5, 3.5, 1.5, 0.5, 0.5, 3.5, 3.5, 1.5] 

这个功能肯定有瑕疵──为一个东西,它不验证参数,它应该更聪明地设置返回值的类型 - 但它具有的优点是,无论数据中的元素是整数还是浮点数还是某种其他数值类型,它都可以工作。另外,默认情况下,它会返回一个整数列表,但是通过传递一个适当的函数作为可选的量化参数,可以将此类型更改为其他值。此外,如果数据参数是一个列表,返回的值将是一个列表;如果数据是一个元组,返回的值将是一个元组。 (这最后一个特性肯定是最弱的一个,但它也是我最不感兴趣的在Java中复制的,所以我没有费心使它更加健壮。)

我想写一个高效的这个函数与Java相当,这意味着要搞清楚如何避免Java的输入。自从我学习Java(以前)以来,泛型被引入到了语言中。我尝试了解Java泛型,但发现它们很难理解。我不知道这是由于早期衰老,还是由于自从我上次编程后(大约2001年)Java复杂性的巨大增长,但是我发现关于此主题的每一页都比以前更混乱一。我真的很感激,如果有人能告诉我如何在Java中做到这一点。

谢谢!

+0

你想要它处理浮动/双打或整体? – jjnguy

+0

那么鸭子式的功能既是缺点又是美德?你要么必须多次写这个方法,一次用于浮点数,一次用于整数等等,否则你将需要使整个类通用于T,并接受此方法中的List 。 – wberry

回答

1

这不完全是你问的,但我可以建议尝试Jython?你可以把你的Python代码直接编译成Java字节码。由于您从2001年起就没有使用过Java,并且您近期似乎正在使用Python,所以您可能会发现Jython更容易处理,而不必事先加速Java中的所有更改。

+0

出于好奇,Jython如何处理鸭子打字? – thegrinner

+0

@thegrinner:与CPython一样。 Jython只是JVM的Python实现。 http://www.jython.org/jythonbook/en/1.0/ObjectOrientedJython.html#protocols(不知道它如何与导入的Java类一起工作)。 – JAB

+0

@thegrinner如果您尝试调用具有不兼容类型的Java方法在运行时,你会得到NoM​​ethodDefFoundException或者其他任何被调用的东西,就好像你在Java代码中使用自省(实际上,它实际上就是Jython封面下发生的事情)。 – wberry

4

输入/输出类型问题的一种解决方案可能是使用Number类及其子类和通配符。如果您想接受任何类型的数字参数,则可以将输入类型指定为Number? extends Number。如果输入是一个列表,则后一种形式具有优势,因为它将确保列表中的每个元素具有相同的类型(它必须是Number的子类)。 ?被称为Wildcard,并且当它被表示为? extends Number时,它是“有界通配符”,并且可能只能引用边界类型的子类型。

实施例:

public List<Number> func(List<? extends Number> data, Number nlevels) 

这将采取的Number一个特定子类的列表,一个Number为NLEVELS参数,并返回的Number小号

列表作为该函数的输入参数,可以输入Method,虽然此时检查类型会变得很困难,因为您会将有界未知参数的数据传递给Method对象。我不完全确定这将如何工作。

至于返回类型,可以指定另一个参数,一个类对象(可能是? extends Number),列表元素将被转换(或转换)为。

public List<? extends Number> quantize(List<? extends Number> data, 
             Number nlevels, 
             Method quantizer, 
             Class<? extends Number> returnType) 

这是尝试在您的函数可能声明在Java中。然而,实施稍微复杂一些。

+0

我想你想要的方法是通用的:'public List func(List data,T nlevels)' – jjnguy

+0

但是他可能会指出返回的值类型可能不一样输入类型。 – ty1824