2014-04-05 50 views
5

Lambda ifadesini kullanarak ortak istatistikleri uygulayan basit bir sınıf oluşturmak istiyorum. Statistic() yöntemindeki anahtar kutusunu kullanmayı nasıl engelleyebilirim?Java 8 refakatçi lambda ifadeleri

Mesela ben

teşekkür ederiz vb listenin varyansı hesaplamak için yeni bir lambda yazmak isteyebilirsiniz.

public class DescriptiveStatistics { 

    public static void main(String[] args) { 
     List<Double> numbers = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); 
     numbers.stream().forEach(n-> System.out.print(n + " ")); 
     System.out.println(); 
     System.out.println("Descriptive statistics"); 
     System.out.println("Sum: " + statistic(numbers, "Sum")); 
     System.out.println("Max: " + statistic(numbers, "Max")); 
     System.out.println("Min: " + statistic(numbers, "Min")); 
     System.out.println("Average: " + statistic(numbers, "Average")); 
     System.out.println("Count: " + statistic(numbers, "Count")); 
    } 

    private static double statistic(List<Double> numbers, String function) { 
     switch (function.toLowerCase()) { 
      case "sum": 
       return numbers.stream().mapToDouble(Double::doubleValue).sum(); 
      case "max": 
       return numbers.stream().mapToDouble(Double::doubleValue).max().getAsDouble(); 
      case "min": 
       return numbers.stream().mapToDouble(Double::doubleValue).min().getAsDouble(); 
      case "average": 
       return numbers.stream().mapToDouble(Double::doubleValue).average().getAsDouble(); 
      case "count": 
       return numbers.stream().mapToDouble(Double::doubleValue).count(); 
     } 
     return 0; 
    } 

Ben DoubleStream ve döner alır fonksiyon tipi ile yöntemin Dize parametresini istatistik değiştirin bu

private static double newStatistics(List<Double> numbers, Function<Double, Double> function){ 
     return numbers.stream().mapToDouble(Double::doubleValue).function(); 
    } 
+0

) veya bir Strateji arayüzü (eğer çalışma zamanında yenilerini takabilmeniz gerekiyorsa). – chrylis

+0

Strateji arayüzünün soruyu cevapladığını düşünüyorum. Teşekkür ederim! – CheJharia

cevap

11

Neden sadece DoubleStream#summaryStatistics kullanabilir veya benzer bir model uygulamak: akışında farklı işlemler için bir anahtarı deyimi kullanmadan aşağıdaki gibi

private static double statistic(List<Double> numbers, 
           ToDoubleFunction<DoubleStream> function) { 
    return function.applyAsDouble(
     numbers.stream().mapToDouble(Double::doubleValue)); 
} 

Şimdi, yöntemi çağırabilirsiniz?

Hatta örneğin bir varyans, çarpıklık ve basıklık demek, özel yöntemleri eklemek sınıfını genişletmek olabilir:

vaktinden tüm işlevleri biliyorsanız (ya bir `enum` kullanıyor olmalıdır
/** 
* Algorithms derived from: Philippe Pébay, Formulas for Robust, One-Pass Parallel 
* Computation of Covariances and Arbitrary-Order Statistical Moments. 
*/ 
public class MoreDoubleStatistics extends DoubleSummaryStatistics { 

    private double M1, M2, M3, M4; 

    @Override 
    public void accept(double x) { 
     super.accept(x); 

     long n = getCount(); 

     double delta = x - M1;      // δ 
     double delta_n = delta/n;     // δ/n 
     double delta2_n = delta * delta_n;   // δ^2/n 
     double delta2_n2 = delta_n * delta_n;  // δ^2/n^2 
     double delta3_n2 = delta2_n * delta_n;  // δ^3/n^2 
     double delta4_n3 = delta3_n2 * delta_n;  // δ^4/n^3 

     M4 += (n - 1) * (n * n - 3 * n + 3) * delta4_n3 
       + 6 * M2 * delta2_n2 
       - 4 * M3 * delta_n; 
     M3 += (n - 1) * (n - 2) * delta3_n2 
       - 3 * M2 * delta_n; 
     M2 += (n - 1) * delta2_n; 
     M1 += delta_n; 
    } 

    @Override 
    public void combine(DoubleSummaryStatistics other) { 
     throw new UnsupportedOperationException(
       "Can't combine a standard DoubleSummaryStatistics with this class"); 
    } 

    public void combine(MoreDoubleStatistics other) { 
     MoreDoubleStatistics s1 = this; 
     MoreDoubleStatistics s2 = other; 

     long n1 = s1.n(); 
     long n2 = s2.n(); 
     long n = n1 + n2; 

     double delta = s2.M1 - s1.M1;    // δ 
     double delta_n = delta/n;     // δ/n 
     double delta2_n = delta * delta_n;   // δ^2/n 
     double delta2_n2 = delta_n * delta_n;  // δ^2/n^2 
     double delta3_n2 = delta2_n * delta_n;  // δ^3/n^2 
     double delta4_n3 = delta3_n2 * delta_n;  // δ^4/n^3 

     this.M4 = s1.M4 + s2.M4 + n1 * n2 * (n1 * n1 - n1 * n2 + n2 * n2) * delta4_n3 
       + 6.0 * (n1 * n1 * s2.M2 + n2 * n2 * s1.M2) * delta2_n2 
       + 4.0 * (n1 * s2.M3 - n2 * s1.M3) * delta_n; 

     this.M3 = s1.M3 + s2.M3 + n1 * n2 * (n1 - n2) * delta3_n2 
       + 3.0 * (n1 * s2.M2 - n2 * s1.M2) * delta_n; 

     this.M2 = s1.M2 + s2.M2 + n1 * n2 * delta2_n; 

     this.M1 = s1.M1 + n2 * delta; 

     super.combine(other); 
    } 

    private long n() { return getCount(); } 

    public double mean() { return getAverage(); } 
    public double variance() { return n() <= 1 ? 0 : M2/(n() - 1); } 
    public double stdDev() { return sqrt(variance()); } 
    public double skew() { return M2 == 0 ? 0 : sqrt(n()) * M3/ pow(M2, 1.5); } 
    public double kurtosis() { return M2 == 0 ? 0 : n() * M4/(M2 * M2) - 3.0; } 
} 
+0

Bir birleştirici işlev için kod sağlayabilir misiniz, böylece bu sınıf bir koleksiyoncu olarak kullanılabilir? –

+0

ancak sonuçta varyansın çarpıklığı ve basıklığı yanlış olmaz mıydı? Ben DoubleSummaryStatistics :: birleştirmek onları dikkate almazsa, ya da bir şey mi eksik? –

+0

@RolandGude Doğru nokta kesinlikle haklısınız. Düzenlenen. – assylias

8

gibi bir yöntemin aklında agrega.

System.out.println("Sum: " + statistic(numbers, s -> s.sum())); 
System.out.println("Max: " + statistic(numbers, s -> s.max().getAsDouble())); 
System.out.println("Min: " + statistic(numbers, s -> s.min().getAsDouble())); 
System.out.println("Average: " + statistic(numbers, s -> s.average().getAsDouble())); 
System.out.println("Count: " + statistic(numbers, s -> s.count())); 
+1

Teşekkür ederim. Kendi ToDoubleFunction() 'ı oluşturma hakkında daha fazla bilgiyi nerede bulabilirim? Örneğin, varyansı hesaplamak için. – CheJharia

+0

's.sum()' yerine 'DoubleStream :: sum' için basitleştirebilirsiniz. Aynı şey s -> s.count() 'için de geçerli. – bcsb1001

+0

@ bcsb1001: Bence -> s.sum() '' DoubleStream :: sum''dan (okunabilirlik ve süreklilik ile ilgili) daha basittir. – nosid

İlgili konular