2011-08-04 45 views
2

我正在通过Norvig's book on AIP进行工作。这里面的练习上编写跨产品功能 -Java中的跨产品计算器

(defun cross-product (fn list-1 list-2) 
    (mappend #'(lambda (y) 
       (mapcar #'(lambda (x) 
          (funcall fn y x)) 
         list-2)) 
      list-1)) 

(defun mappend (fn the-list) 
    (if (null the-list) 
     nil 
     (append (funcall fn (first the-list)) 
       (mappend fn (rest the-list))))) 

我试图用Java编写的实现 -

interface Function<T1, T2, T3> { 
    public T3 function(T1 t1, T2 t2); 
} 

public class CrossProduct<T1, T2> { 
    private List<T1> list1; 
    private List<T2> list2; 

    public CrossProduct(List<T1> t1, List<T2> t2) { 
     this.list1 = t1; 
     this.list2 = t2; 
    } 

    public <T3> List<T3> calculate(Function<T1, T2, T3> fn) { 
    List product = new ArrayList(); 
    for (int i = 0; i < list1.size(); i++) 
     for (int j = 0; j < list2.size(); j++) 
      product.add(fn.function(list1.get(i), list2.get(j))); 
    return product; 
} 

}

用法 -

@Test 
public void testWithStrings() { 
    List<String> list1 = new ArrayList<String>(); 
    list1.add("6"); 
    list1.add("8"); 

    List<String> list2 = new ArrayList<String>(); 
    list2.add("2"); 
    list2.add("3"); 

    List<String> product = new CrossProduct<String, String>(list1, list2) 
      .<String> calculate(new Function<String, String, String>() { 
       public String function(String x, String y) { 
        return (String) x + (String) y; 
       } 

      }); 

    Assert.assertEquals("62", product.get(0)); 
    Assert.assertEquals("63", product.get(1)); 
    Assert.assertEquals("82", product.get(2)); 
    Assert.assertEquals("83", product.get(3)); 
} 

有没有更好的方法来做到这一点?

回答

1

这样看起来有些随心所欲地定义你的CrossProduct类:为什么列表是参数成员变量,而fn是一个方法参数?事实上,为什么CrossProduct是一个班级呢?跨产品is a名单,但它不是列表的一个亚型,因为给定的名单可以既

  1. 被表示为许多不同的方式跨产品,并
  2. 没有被使用crossproduct功能构成。

将“交叉产品”视为一种类型,IMO并不自然。

我可能会做一些像

public class ListFunctions { 
    public static <T1, T2, T3> List<T3> crossProduct(List<T1> list1, List<T2> list2, Function<T1, T2, T3> fn) { 
     List<T3> product = new ArrayList<T3>(); 
     for (int i = 0; i < list1.size(); i++) 
      for (int j = 0; j < list2.size(); j++) 
      product.add(fn.function(list1.get(i), list2.get(j))); 
     return product; 
    } 
} 

如果你确实想定义一个类CrossProduct出于某种原因(例如,为了实现如萨尔曼建议懒惰的评价),我会说这是更多的面向对象有三个作为成员变量的参数,并且使类实现List,例如

public class CrossProduct<T1, T2, T3> implements List<T3> { 
    public CrossProduct(T1 list1, T2 list2, Function<T1, T2, T3> fn) { 
     // remember args... 
    } 
    // etc... 
} 
+0

谢谢!两者都是改进。 – user869081

1

我不知道你想要改进哪些参数。不过,我会说我不喜欢N * M的名单,因为它可能太大了。如果我知道结果列表可能是不可变的,那么我会实现我自己的列表,它只在调用result.get(i*M+j-1)时计算product(l1(i), l2(j))。所以我没有保留很长的列表(如果需要,也许只是一个小缓存)。