Кросс-продукт калькулятор на Java
Я работаю над книгой Норвига об AIP. В нем есть упражнение по написанию функции перекрестного произведения -
(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));
}
Есть ли лучший способ сделать это?
2 ответа
Кажется, немного произвольно определить ваш CrossProduct
класс таким образом: почему список аргументов членов переменных, тогда как fn
такое параметр метода? На самом деле, почему CrossProduct
класс вообще? Кросс-продукт is a
список, но это не подтип списка, поскольку данный список может
- быть выраженным как перекрестный продукт по-разному, и
- не были построены с использованием
crossproduct
функция.
Неестественно думать о "перекрестном продукте" как о типе, ИМО.
Я бы наверное сделал что-то вроде
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...
}
Я не знаю точно, какие параметры вы хотели бы улучшить. Однако я бы сказал, что мне не нравится размер списка N*M, поскольку он может быть слишком большим. Если бы я знал, что список результатов может быть неизменным, то я бы реализовал свой собственный список, который рассчитывает только product(l1(i), l2(j))
когда result.get(i*M+j-1)
называется. Поэтому я не держу длинный список (возможно, просто небольшой кеш, если это необходимо).