Повторное использование кода - Java - PullRequest
3 голосов
/ 18 января 2011

Есть ли способ повторного использования итерации через код массива в этих функциях:

public static double[] ln(double[] z) {
    // returns  an array that consists of the natural logarithm of the values in array z
    int count = 0;

    for (double i : z){
        z[count] = Math.log(i);
        count += 1;
    }
    return z;
}


public static double[] inverse(double[] z) {
    //  returns  an array that consists of the inverse of the values in array z
    int count = 0;

    for (double i : z){
        z[count] = Math.pow(i,-1);
        count += 1;
    }
    return z;
}

Ответы [ 7 ]

7 голосов
/ 18 января 2011

Да, используя http://en.wikipedia.org/wiki/Strategy_pattern, но это, вероятно, будет излишним.Повторяющаяся итерация и число ++ не выглядят так плохо.

3 голосов
/ 18 января 2011

Ваш код не соответствует вашим комментариям.

public static double[] ln(double[] z) {
    // returns  an array that consists of the natural logarithm of the values in array z
    double[] r = new double[z.length];
    for(int i=0;i<z.length;i++) r[i] = Math.log(z[i]);
    return r;
}

public static double[] inverse(double[] z) {
    //  returns  an array that consists of the inverse of the values in array z
    double[] r = new double[z.length];
    for(int i=0;i<z.length;i++) r[i] = 1/z[i];
    return r;
}

Вы можете использовать шаблон стратегии, чтобы сделать цикл общим, но у этого есть три нижние стороны.

  • Кодгораздо труднее читать.
  • Код длиннее и сложнее.
  • Выполнение во много раз медленнее.

Math.pow (x, -1) во много раз дороже, чем 1 / x, и может привести к большей ошибке округления.

2 голосов
/ 18 января 2011

Вот способ повторно использовать цикл. Вот пример:

public interface MathOperation {
   public double f(double value);
}

public class Invert implements MathOperation {
   public double f(double value) {
     return Math.pow(value, -1);
   }
}

public class Log implements MathOperation {
   public double f(double value) {    
     return Math.log(value);
   }
}

private static void calculateOnArray(double[] doubles, MathOperation operation) {
  int count = 0;

  for (double i : doubles){
    doubles[count] = operation.f(i);
    count += 1;
  }
}

public static double[] ln(double[] z) {
  calculateOnArray(z, new Log());
  return z;
}

public static double[] inverse(double[] z) {
  calculateOnArray(z, new Invert());
  return z;
}

Примечание - это не шаблон команды , но также не реализация шаблона стратегии real . Это близко к Стратегии, но чтобы избежать дальнейших понижений, я сохраняю шаблон без имени ; -)

1 голос
/ 18 января 2011

Да, как пара примеров здесь иллюстрируют. Но если это реальная проблема, а не крайнее упрощение, то создание трех классов с наследованием и перегрузкой для экономии при написании одного оператора FOR, мне кажется, движется в неправильном направлении. Вы пишете двадцать строк кода, чтобы сэкономить на повторении одной строки кода. В чем выгода?

Если в реальной жизни процесс итерации по данным по какой-то причине намного сложнее - если это не просто цикл по массиву, а, я не знаю, какой-то сложный процесс поиска в базе данных отправив запрос веб-службы и выполнив страницу, полную сложных вычислений, чтобы найти следующий элемент - тогда мой ответ будет другим.

1 голос
/ 18 января 2011

Вы можете использовать это повторно, если используете функциональное программирование.Существует множество библиотек, в которых написано много материала для вас.В этом примере я просто использовал Guava (http://code.google.com/p/guava-libraries/),, но библиотеки, такие как Functional Java (http://functionaljava.org/), будут работать так же хорошо.

). Одним из основных преимуществ функционального программирования является возможность абстрагировать алгоритмтак что вы можете использовать этот алгоритм везде, где вам нужно. Для вашего случая ваш алгоритм по сути такой:

  1. Для каждого элемента в списке
  2. Выполните операцию x для этого элемента
  3. Возврат нового списка

Хитрость заключается в том, чтобы передать новую операцию вместо «x» в зависимости от того, что вы хотите сделать. В мире функционального программирования мысоздайте объект с именем «функтор», который на самом деле является просто способом инкапсуляции функции в объекте (если бы в Java были замыкания, это было бы еще проще, но это то, что мы имеем).

В любом случаеВот немного кода, который будет делать то, что вы хотите:

Класс: обратный


import com.google.common.base.Function;

public class Inverse implements Function
{
    @Override
    public Double apply(Double arg0)
    {
        return Math.pow(arg0, -1);
    }
}

Класс: логарифм


import com.google.common.base.Function;

public class Logarithm implements Function
{
    @Override
    public Double apply(Double arg0)
    {
        return Math.log(arg0);
    }
}

Класс: драйвер


import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.Test;

import com.google.common.collect.Collections2;

public class Driver
{
    @Test
    public void testInverse()
    {
        List initialValues = Arrays.asList(new Double[] {1.0, 2.0, 3.0});

        List logValues = new ArrayList(Collections2.transform(initialValues, new Inverse()));

        assertEquals(3, logValues.size());

        assertEquals(Double.valueOf(1.0), logValues.get(0), 0.01);
        assertEquals(Double.valueOf(0.5), logValues.get(1), 0.01);
        assertEquals(Double.valueOf(0.333), logValues.get(2), 0.01);

    }
}

Водитель класs просто содержит один контрольный пример, но он иллюстрирует использование Функторов, определенных выше.Вы заметите использование метода Collections2.transform в методе testInverse выше.Документацию по этому методу можно найти здесь:

http://guava -libraries.googlecode.com / svn / trunk / javadoc / com / google / common / collect / Collections2.html # transform% 28java.util.Collection,% 20com.google.common.base.Function% 29

По сути, этот метод абстрагируется от алгоритма перехода по списку и применения некоторой функции к каждому элементуэтот список - все, что вам нужно сделать, это применить функцию для выполнения.Итак, в этом случае я передаю этому методу список двойников и функтор (Inverse).Это возвращает мне новый список, который содержит инверсию каждого значения в начальном списке.Если вы предпочитаете делать записи каждого значения, передайте другой функтор в метод преобразования.

Этот тип программирования имеет небольшую кривую обучения, но он может быть совершенно потрясающим для типаповторное использование кода вы хотите сделать.А используя существующие библиотеки (например, Guava), вы можете сделать свой код еще проще для написания.Обратите внимание, что код, который я написал, на самом деле немного проще, чем тот, который вы написали, потому что мне не приходится иметь дело со списками / массивами при выполнении моих математических функций - я просто выполняю его на одном элементе;функция преобразования заботится о применении этой функции ко всему списку.

1 голос
/ 18 января 2011

В языках, которые поддерживают замыкания, или лямбда-выражения, или блоки, которые можно передавать (например, Ruby), вы можете сделать это кратко. В Java вы можете смоделировать это, определив интерфейс с методом, который должен вызываться как обратный вызов. В местах, где вы его используете, вы создаете анонимный класс, используя интерфейс. Это выглядит немного громоздко:

public interface Calculation {
    double calculate(double x);
}

public static double[] calcArray(double[] z, Calculcation calc) {
    int count = 0;

    for (double i : z){
        z[count] = calc.calculate(i);
        count += 1;
    }
    return z;
}

public static double[] ln(double[] z) {
    return calcArray(z, new Calculation() {
        double calculate(double x) {
            return Math.log(x);
        }
    });
}

public static double[] inverse(double[] z) {
    return calcArray(z, new Calculation() {
        double calculate(double x) {
            return Math.pow(x, -1);
        }
    });
}
1 голос
/ 18 января 2011

Не столько повторное использование, но здесь два момента - во-первых, никогда не используйте i в качестве значения массива, это соглашение, что это итератор, и вы будете путать людей без конца, если вы сделаете это.Во-вторых, вы бы также использовали цикл for, а не цикл для каждого цикла, который уберет вашу ручную переменную count.

Я бы также создал копию массива перед его возвратом,вы на самом деле изменяете передаваемый здесь параметр, который, если кто-то еще видел сигнатуру вашего метода, вероятно, не соответствует ожиданиям.Я ожидаю, что метод с возвращаемым типом оставит исходный параметр без изменений (поскольку нет смысла возвращать то же значение, на которое у меня уже есть ссылка!)

...