重用代码 - Java

发布于 2024-10-12 10:58:43 字数 549 浏览 7 评论 0原文

有没有办法在这些函数中重用数组代码的迭代:

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;
}

Is there any way of reusing the iteration through the array code in these functions:

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

铃予 2024-10-19 10:58:43

是的,使用 http://en.wikipedia.org/wiki/Strategy_pattern 但它可能会太过分了。重复的迭代和 count++ 看起来并没有那么糟糕。

Yes, using http://en.wikipedia.org/wiki/Strategy_pattern but it'll probably be an overkill. A duplicate iteration and count++ doesn't look so bad.

浮生未歇 2024-10-19 10:58:43

您的代码与您的评论不符。

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 昂贵许多倍,并且可能产生更多舍入误差。

Your code doesn't match your comments.

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;
}

You can use a strategy pattern to make the loop generic but this has three down sides.

  • The code is much harder to read.
  • The code is longer and more complex.
  • The execution is many times slower.

Math.pow(x, -1) is many times more expensive than 1/x and can incurr more rounding error.

翻身的咸鱼 2024-10-19 10:58:43

这是重用循环的方法。下面是一个示例:

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;
}

注意 - 这不是命令模式,也不是真实的实现 策略模式。它接近策略,但为了避免进一步的否决,我保留该模式未命名;-)

Here's a way to reuse the loop. Here's an example:

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;
}

Note - this is not the command pattern but not an implementation of the real strategy pattern either. It's close to Strategy, but to avoid further downvotes, I keep the pattern unnamed ;-)

暗恋未遂 2024-10-19 10:58:43

没有太多的重用,但这里有两点 - 首先,永远不要使用 i 作为数组值,按照惯例,它是迭代器,如果你这样做,你会让人们感到困惑。其次,您最好在此处使用 for 循环而不是 for every 循环,这将消除您的手动“count”变量。

我还会在返回数组之前创建一个数组的副本,您实际上正在更改此处传递的参数,如果其他人看到您的方法签名可能不是他们所期望的。我希望具有这样的返回类型的方法能够保持原始参数不变(因为返回与我已经引用的值相同的值是没有意义的!)

Not so much reusing, but two points here - firstly, never use i as an array value, it's convention that it's the iterator and you'll confuse people no end if you do this. Secondly, you'd be as well using a for loop rather than a for each loop here which will take away your manual "count" variable.

I'd also create a copy of the array before returning it, you're actually changing the parameter being passed in here which, if someone else saw your method signature probably isn't what they'd expect. I'd expect a method with a return type like that to leave the original parameter untouched (since there's no point in returning the same value that I've got a reference to already!)

她如夕阳 2024-10-19 10:58:43

在支持闭包、lambda 表达式或可传递块的语言中(例如 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);
        }
    });
}

In languages that support closures, or lambda expressions, or blocks which can be passed (Ruby for example) you could do this concisely. In Java, you could simulate this by defining an interface with a method that is to be called as a callback. In places where you use it, you create an anonymous class using the interface. It looks a bit cumbersome:

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);
        }
    });
}
萌逼全场 2024-10-19 10:58:43

如果您使用函数式编程,则可以重用它。有很多图书馆都为您提供了大量此类内容。在这个例子中,我只是使用了 Guava (http://code.google.com/p/guava-libraries/),但是像 Function Java (http://functionjava.org/) 这样的库也可以工作。

函数式编程的主要好处之一是能够抽象出算法,以便您可以在需要的地方重用该算法。对于您的情况,您的算法本质上是这样的:

  1. 对于列表中的每个元素
  2. 对该元素执行 x 操作
  3. 返回新列表

技巧是能够根据您想要执行的操作传入新操作来代替“x” 。在函数式编程的世界中,我们创建一个称为“函子”的对象,它实际上只是将函数封装在对象中的一种方式。 (如果 Java 有闭包,这会更容易,但这就是我们所拥有的。)

无论如何,这里有一些代码可以完成您想要的操作:

Class: Inverse


import com.google.common.base.Function;

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

Class: Logarithm


import com.google.common.base.Function;

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

Class: Driver


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);

    }
}

Driver 类只包含一个测试用例,但它确实说明了上面定义的函子的用法。您会注意到上面的 testInverse 方法中使用了名为 Collections2.transform 的方法。您可以在此处找到此方法的文档:

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

本质上,此方法的作用是抽象出遍历列表并将某些函数应用于列表中每个元素的算法该列表 - 您所要做的就是应用要执行的功能。因此,在这种情况下,我向该方法传递一个双精度列表和一个函子(逆)。它给我返回一个新列表,其中包含初始列表中每个值的倒数。如果您希望对每个值进行记录,请将不同的函子传递给转换方法。

这种类型的编程确实有一点学习曲线,但对于您想要执行的代码重用类型来说,它绝对是很棒的。通过利用现有的库(如 Guava),您可以使代码更容易编写。请注意,我编写的代码实际上比您编写的代码要容易一些,因为在执行数学函数时我不必处理列表/数组 - 我只需在一个元素上执行;变换函数负责在整个列表中应用该函数。

You can reuse this if you utilize functional programming. There are a number of libraries out there that have a lot of this stuff written for you. For this example, I simply used Guava (http://code.google.com/p/guava-libraries/), but libraries such as Functional Java (http://functionaljava.org/) would work just as well.

One of the major benefits of functional programming is the capability to abstract away an algorithm so that you can reuse that algorithm wherever you need. For your case, your algorithm is essentially this:

  1. For each element in a list
  2. Do operation x to that element
  3. Return the new list

The trick is being able to pass in a new operation in place of "x" depending on what you want to do. In the world of functional programming, we create an object called a "functor", which is really just a way to encapsulate a function in an object. (If Java had closures, this would be even easier, but this is what we have.)

Anyway, here's a bit of code that will do what you want:

Class: Inverse


import com.google.common.base.Function;

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

Class: Logarithm


import com.google.common.base.Function;

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

Class: Driver


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);

    }
}

The Driver class just contains a single test case, but it does illustrate the usage of the Functors defined above. You'll notice the use of a method called Collections2.transform in the testInverse method above. You can find the documentation for this method here:

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

Essentially, what this method does is abstract away the algorithm of stepping over a list and applying some function to each element of that list - all you have to do is apply the function to perform. So, in this case, I pass to that method a list of Doubles and a functor (Inverse). It gives me back a new list that contains the inverse of every value in the initial list. If you would rather do logs of each value, pass a different functor to the transform method.

This type of programming does come with a little bit of a learning curve, but it can be absolutely awesome for the type of code reuse you want to do. And by leveraging existing libraries (like Guava), you can make your code even easier to write. Notice the code I wrote is actually a little easier than what you had written because I don't have to deal with lists/arrays when performing my mathematical functions - I just perform it on one element; the transform function takes care of applying that function across an entire list.

嗼ふ静 2024-10-19 10:58:43

是的,正如这里的几个例子所示。但如果这是真正的问题而不是一种极端的简化,那么创建三个具有继承和重载的类以节省编写一个 FOR 语句在我看来似乎正朝着错误的方向前进。您正在编写二十行代码,以节省重复一行代码的次数。有什么收获?

如果在现实生活中,由于某种原因,迭代数据的过程要复杂得多——如果它不仅仅是循环遍历数组,而且我不知道,还有一些在数据库中查找内容并提交网络的复杂过程服务请求并执行一个充满复杂计算的页面来查找下一个元素 - 那么我的答案会有所不同。

Yes, as a couple of examples here illustrate. But if this is the real problem and not an extreme simplification, creating three classes with inheritance and overloading to save on writing one FOR statement seems to me to be heading in the wrong direction. You're writing twenty lines of code to save on repeating one line of code. What's the gain?

If in real life the process of iterating through the data is much more complicated for some reason -- if it's not just looping through an array but, I don't know, some complex process of looking things up in a database and submitting a web services request and doing a page full of complex calculations to find the next element -- then my answer would be different.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文