क्या जावा करी का समर्थन करता है?


88

मैं सोच रहा था कि क्या जावा में कोई रास्ता है। मुझे लगता है कि बंदों के मूल समर्थन के बिना यह संभव नहीं है।


4
रिकॉर्ड के लिए, जावा 8 अब करी और आंशिक अनुप्रयोग का समर्थन करता है और बंद करने के लिए देशी समर्थन करता है। यह एक बेतहाशा पुराना सवाल है।
रॉबर्ट फिशर

जवाबों:


145

जावा 8 (18 मार्च 2014 को रिलीज़) करी का समर्थन करता है। मिसिंग कोड मिसिंगफैक्टर द्वारा उत्तर में पोस्ट किए गए उदाहरण को फिर से लिखा जा सकता है:

import java.util.function.*;
import static java.lang.System.out;

// Tested with JDK 1.8.0-ea-b75
public class CurryingAndPartialFunctionApplication
{
   public static void main(String[] args)
   {
      IntBinaryOperator simpleAdd = (a, b) -> a + b;
      IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;

      // Demonstrating simple add:
      out.println(simpleAdd.applyAsInt(4, 5));

      // Demonstrating curried add:
      out.println(curriedAdd.apply(4).applyAsInt(5));

      // Curried version lets you perform partial application:
      IntUnaryOperator adder5 = curriedAdd.apply(5);
      out.println(adder5.applyAsInt(4));
      out.println(adder5.applyAsInt(6));
   }
}

... जो काफी अच्छा है। व्यक्तिगत रूप से, जावा 8 उपलब्ध होने के साथ मैं एक वैकल्पिक जेवीएम भाषा जैसे स्काला या क्लोजर का उपयोग करने का बहुत कम कारण देखता हूं। वे निश्चित रूप से अन्य भाषा सुविधाएँ प्रदान करते हैं, लेकिन यह संक्रमण लागत और कमजोर आईडीई / टूलींग / पुस्तकालयों के समर्थन, आईएमओ को सही ठहराने के लिए पर्याप्त नहीं है।


11
मैं जावा 8 से प्रभावित हूं, लेकिन कार्यात्मक भाषा सुविधाओं से परे क्लोजर एक सम्मोहक मंच है। क्लॉजुर अत्यधिक कुशल, अपरिवर्तनीय डेटा संरचना और परिष्कृत संगामिति तकनीक जैसे सॉफ्टवेयर-ट्रांसेक्शनल मेमोरी प्रदान करता है।
माइकल ईस्टर

2
समाधान के लिए धन्यवाद, भाषा डिग के लिए इतना नहीं :) :) कमजोर आईडीई समर्थन एक मुद्दा है, लेकिन टूलींग / लाइब्रेरीज़ स्पष्ट कटौती नहीं है - क्लोजर के बाद जावा 8 पर वापस चला गया, मैं वास्तव में टूल मिडजे, कोर जैसे लाइब्रेरियों को याद कर रहा हूं .async, और मैक्रोज़ और आसान कार्यात्मक वाक्यविन्यास जैसी भाषा सुविधाएँ। क्लोजर में करी उदाहरण:(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)
कोर्न

5
क्लोजर एक महान भाषा हो सकती है, लेकिन समस्या यह है कि यह सिर्फ जावा डेवलपर्स के बहुमत के लिए "एलियन" है जो केवल पारंपरिक सी-स्टाइल सिंटैक्स के लिए उपयोग किया जाता है; भविष्य में क्लोजर (या किसी अन्य वैकल्पिक जेवीएम भाषा) के लिए एक महत्वपूर्ण प्रवासन देखना बहुत कठिन है, विशेष रूप से यह देखते हुए कि ऐसी कई भाषाएं पहले से ही कई सालों से मौजूद हैं और ऐसा नहीं हुआ है (.NET दुनिया में वही घटना होती है) जहाँ F # जैसी भाषाएँ हाशिए पर रहती हैं)।
रोजेरियो

2
मुझे नीचा दिखाना होगा, क्योंकि यह साधारण मामला दिखाता है। एक प्रयास करें जो स्ट्रिंग से आपकी खुद की कक्षा बनाता है जो फिर किसी अन्य वर्ग में परिवर्तित होता है, और कोड की मात्रा की तुलना करता है
M4ks

11
@ M4ks सवाल केवल यह है कि जावा क्यूरिंग का समर्थन करता है या नहीं, यह अन्य भाषाओं की तुलना में कोड की मात्रा के बारे में नहीं है।
रोजेरियो

67

जावा में करी और आंशिक अनुप्रयोग पूरी तरह से संभव है, लेकिन आवश्यक कोड की मात्रा शायद आपको बंद कर देगी।


जावा में करी और आंशिक अनुप्रयोग प्रदर्शित करने के लिए कुछ कोड:

interface Function1<A, B> {
  public B apply(final A a);
}

interface Function2<A, B, C> {
  public C apply(final A a, final B b);
}

class Main {
  public static Function2<Integer, Integer, Integer> simpleAdd = 
    new Function2<Integer, Integer, Integer>() {
      public Integer apply(final Integer a, final Integer b) {
        return a + b;
      }
    };  

  public static Function1<Integer, Function1<Integer, Integer>> curriedAdd = 
    new Function1<Integer, Function1<Integer, Integer>>() {
      public Function1<Integer, Integer> apply(final Integer a) {
        return new Function1<Integer, Integer>() {
          public Integer apply(final Integer b) {
            return a + b;
          }
        };
      }
    };

  public static void main(String[] args) {
    // Demonstrating simple `add`
    System.out.println(simpleAdd.apply(4, 5));

    // Demonstrating curried `add`
    System.out.println(curriedAdd.apply(4).apply(5));

    // Curried version lets you perform partial application 
    // as demonstrated below.
    Function1<Integer, Integer> adder5 = curriedAdd.apply(5);
    System.out.println(adder5.apply(4));
    System.out.println(adder5.apply(6));
  }
}

FWIW यहाँ जावा कोड के ऊपर हास्केल बराबर है:

simpleAdd :: (Int, Int) -> Int
simpleAdd (a, b) = a + b

curriedAdd :: Int -> Int -> Int
curriedAdd a b = a + b

main = do
  -- Demonstrating simpleAdd
  print $ simpleAdd (5, 4)

  -- Demonstrating curriedAdd
  print $ curriedAdd 5 4

  -- Demostrating partial application
  let adder5 = curriedAdd 5 in do
    print $ adder5 6
    print $ adder5 9

@OP: दोनों निष्पादन योग्य कोड स्निपेट हैं और आप उन्हें ideone.com पर आज़मा सकते हैं।
लापता

16
यह उत्तर जावा 8 की रिलीज के बाद से पुराना है। अधिक संक्षिप्त तरीके के लिए रोजेरियो का जवाब देखें।
मथायस ब्रौन

15

जावा 8 के साथ करी करने के लिए बहुत सारे विकल्प हैं। फ़ंक्शन प्रकार Javaslang और jOOλ दोनों बॉक्स से बाहर करी की पेशकश करते हैं (मुझे लगता है कि यह JDK में एक निरीक्षण था), और Cyclops फ़ंक्शंस मॉड्यूल में करी JDK फ़ंक्शंस के लिए स्थिर तरीकों का एक सेट है और विधि संदर्भ। उदाहरण के लिए

  Curry.curry4(this::four).apply(3).apply(2).apply("three").apply("4");

  public String four(Integer a,Integer b,String name,String postfix){
    return name + (a*b) + postfix;
 }

'क्यूरिंग' कंज्यूमर्स के लिए भी उपलब्ध है। उदाहरण के लिए 3 पैरामेट्स के साथ एक विधि वापस करने के लिए, और उनमें से 2 पहले से ही लागू हैं हम कुछ इसी तरह करते हैं

 return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);

जावाडोक


IMO, यह वही है जिसे वास्तव curryingमें Curry.currynसोर्स कोड में कहा जाता है ।
लेबेका

13

संपादित करें : 2014 और जावा 8 के रूप में, जावा में कार्यात्मक प्रोग्रामिंग अब न केवल संभव है, बल्कि बदसूरत भी नहीं है (मैं सुंदर कहने की हिम्मत करता हूं)। उदाहरण के लिए देखें रोजेरियो का जवाब

पुराना उत्तर:

यदि आप कार्यात्मक प्रोग्रामिंग तकनीकों का उपयोग करने जा रहे हैं, तो जावा सबसे अच्छा विकल्प नहीं है। जैसा कि लापताफैक्टर ने लिखा है, आप जो चाहते हैं उसे प्राप्त करने के लिए आपको बड़ी मात्रा में कोड लिखना होगा।

दूसरी ओर, आप JVM पर जावा के लिए प्रतिबंधित नहीं हैं - आप स्काला या क्लोजर का उपयोग कर सकते हैं जो कार्यात्मक भाषाएं हैं (स्काला वास्तव में, कार्यात्मक और ओओ दोनों हैं)।


8

करीने से एक फ़ंक्शन वापस करना पड़ता है । यह जावा (कोई फ़ंक्शन पॉइंटर्स) के साथ संभव नहीं है, लेकिन हम एक प्रकार को परिभाषित और वापस कर सकते हैं जिसमें फ़ंक्शन विधि शामिल है:

public interface Function<X,Z> {  // intention: f(X) -> Z
   public Z f(X x);
}

अब एक साधारण विभाजन को करीने दें । हमें एक विभक्त चाहिए :

// f(X) -> Z
public class Divider implements Function<Double, Double> {
  private double divisor;
  public Divider(double divisor) {this.divisor = divisor;}

  @Override
  public Double f(Double x) {
    return x/divisor;
  }
}

और एक विभाजन :

// f(x) -> g
public class DivideFunction implements Function<Double, Function<Double, Double>> {
  @Override
  public function<Double, Double> f(Double x) {
    return new Divider(x);
  }

अब हम एक करी विभाजन कर सकते हैं:

DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.);  // calculates f(1,2) = 0.5

1
अब जब मैंने अपना उदाहरण समाप्त कर लिया है (स्क्रैच से विकसित) यह पता चला है, कि मिसिंगकोड उत्तर का एकमात्र अंतर यह है कि मैं अनाम कक्षाओं का उपयोग करता हूं;)
एंड्रियास डॉक

1
@missingfaktor - mea culpa;)
एंड्रियास

5

खैर, स्काला , Clojure या Haskell (या किसी अन्य कार्यात्मक प्रोग्रामिंग भाषा ...) निश्चित रूप से कर रहे हैं भाषाओं currying और अन्य कार्यात्मक चाल के लिए उपयोग करने के लिए।

कहा गया है कि बॉयलर की सुपर मात्रा के बिना जावा के साथ करी करना निश्चित रूप से संभव है, कोई भी उम्मीद कर सकता है (अच्छी तरह से, प्रकार के बारे में स्पष्ट होने से बहुत दर्द होता है - बस curriedउदाहरण पर गौर करें ;;)।

टेस्ट बाइलो शोकेस दोनों, एक Function3में करीFunction1 => Function1 => Function1 :

@Test
public void shouldCurryFunction() throws Exception {
  // given
  Function3<Integer, Integer, Integer, Integer> func = (a, b, c) -> a + b + c;

  // when
  Function<Integer, Function<Integer, Function<Integer, Integer>>> cur = curried(func);

  // then
  Function<Integer, Function<Integer, Integer>> step1 = cur.apply(1);
  Function<Integer, Integer> step2 = step1.apply(2);
  Integer result = step2.apply(3);

  assertThat(result).isEqualTo(6);
}

और साथ ही साथ आंशिक अनुप्रयोग , हालांकि यह वास्तव में इस उदाहरण में नहीं है:

@Test
public void shouldCurryOneArgument() throws Exception {
  // given
  Function3<Integer, Integer, Integer, Integer> adding = (a, b, c) -> a + b + c;

  // when
  Function2<Integer, Integer, Integer> curried = applyPartial(adding, _, _, put(1));

  // then
  Integer got = curried.apply(0, 0);
  assertThat(got).isEqualTo(1);
}

यह एक सबूत के कॉन्सेप्ट से लिया गया है जिसे मैंने कल एक घंटे में जावाऑन से पहले मज़े के लिए लागू किया है "क्योंकि मैं ऊब गया था" ;-) यहाँ कोड उपलब्ध है: https://github.com/ktoso/jcurry

सामान्य विचार का विस्तार फंक्शनएन => फंक्शनएम तक किया जा सकता है, अपेक्षाकृत आसानी से, हालांकि "वास्तविक टाइपफ़ेक्टी" पार्टिया एप्लीकेशन उदाहरण के लिए एक समस्या बनी हुई है और करी उदाहरण में बॉयलरप्लेटटी कोड के एक नरक बहुत की आवश्यकता होगी जर्करी बायलरप्लाट्टी , लेकिन यह उल्लेखनीय है।

सब सब में, यह संभव है, अभी तक स्काला में यह बॉक्स से बाहर है;;


5

जावा 7 मेथडलैंड्स के साथ करीने का अनुकरण कर सकते हैं: http://www.tutorials.de/threads/java-7-currying-mit-methodhandles.392397/

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleCurryingExample {
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle sum = lookup.findStatic(Integer.class, "sum", MethodType.methodType(int.class, new Class[]{int.class, int.class}));
        //Currying
        MethodHandle plus1 = MethodHandles.insertArguments(sum,0,1);
        int result = (int) plus1.invokeExact(2);
        System.out.println(result); // Output: 3
    }
}

5

हां, अपने लिए कोड उदाहरण देखें:

import java.util.function.Function;

public class Currying {

    private static Function<Integer, Function<Integer,Integer>> curriedAdd = a -> b -> a+b ;

    public static void main(String[] args) {

        //see partial application of parameters
        Function<Integer,Integer> curried = curriedAdd.apply(5);
        //This partial applied function can be later used as
        System.out.println("ans of curried add by partial application: "+ curried.apply(6));
        // ans is 11

        //JS example of curriedAdd(1)(3)
        System.out.println("ans of curried add: "+ curriedAdd.apply(1).apply(3));
        // ans is 4

    }

}

यह curriedAdd के साथ एक सरल कार्य है, जो एक अन्य फ़ंक्शन देता है, और इसे curried में संग्रहीत पैरामीटर के आंशिक अनुप्रयोग के लिए उपयोग किया जा सकता है होता है जो अपने आप में एक फ़ंक्शन है। यह अब बाद में पूरी तरह से लागू होता है जब हम इसे स्क्रीन पर प्रिंट करते हैं।

इसके अलावा, बाद में आप देख सकते हैं कि आप इसे किस तरह की जेएस शैली में उपयोग कर सकते हैं

curriedAdd.apply(1).apply(2) //in Java
//is equivalent to 
curriedAdd(1)(2) // in JS

4

एक और जावा 8 संभावनाओं पर ले लो:

BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;

Function<Integer, Integer> increment = y -> add.apply(1, y);
assert increment.apply(5) == 6;

आप इस तरह से उपयोगिता विधियों को परिभाषित कर सकते हैं:

static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
    return a2 -> f.apply(a1, a2);
}

जो आपको यकीनन अधिक पठनीय वाक्य रचना प्रदान करता है:

Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;

3

जावा में एक विधि को बनाना हमेशा संभव होता है, लेकिन यह मानक तरीके से इसका समर्थन नहीं करता है। इसे प्राप्त करने की कोशिश जटिल है और कोड को अपठनीय बनाता है। जावा इसके लिए उपयुक्त भाषा नहीं है।


3

जावा 6+ के लिए एक और विकल्प यहां है

abstract class CurFun<Out> {

    private Out result;
    private boolean ready = false;

    public boolean isReady() {
        return ready;
    }

    public Out getResult() {
        return result;
    }

    protected void setResult(Out result) {
        if (isReady()) {
            return;
        }

        ready = true;
        this.result = result;
    }

    protected CurFun<Out> getReadyCurFun() {
        final Out finalResult = getResult();
        return new CurFun<Out>() {
            @Override
            public boolean isReady() {
                return true;
            }
            @Override
            protected CurFun<Out> apply(Object value) {
                return getReadyCurFun();
            }
            @Override
            public Out getResult() {
                return finalResult;
            }
        };
    }

    protected abstract CurFun<Out> apply(final Object value);
}

तब आप इस तरह से करी हासिल कर सकते हैं

CurFun<String> curFun = new CurFun<String>() {
    @Override
    protected CurFun<String> apply(final Object value1) {
        return new CurFun<String>() {
            @Override
            protected CurFun<String> apply(final Object value2) {
                return new CurFun<String>() {
                    @Override
                    protected CurFun<String> apply(Object value3) {
                        setResult(String.format("%s%s%s", value1, value2, value3));
//                        return null;
                        return getReadyCurFun();
                    }
                };
            }
        };
    }
};

CurFun<String> recur = curFun.apply("1");
CurFun<String> next = recur;
int i = 2;
while(next != null && (! next.isReady())) {
    recur = next;
    next = recur.apply(""+i);
    i++;
}

// The result would be "123"
String result = recur.getResult();

2

जब आप जावा में करी कर सकते हैं, तो यह बदसूरत है (क्योंकि इसका समर्थन नहीं किया गया है) जावा में यह सादे छोरों और सरल अभिव्यक्तियों का उपयोग करने के लिए सरल और तेज है। यदि आप एक उदाहरण पोस्ट करते हैं कि आप करीने का उपयोग कहां करेंगे, तो हम विकल्प का सुझाव दे सकते हैं जो समान कार्य करते हैं।


3
लूप के साथ करी करने के लिए क्या मिला है ?! इससे पहले कि आप इसके बारे में एक प्रश्न का उत्तर दें, कम से कम शब्द देखें।
लापता

@missingFaktor, आमतौर पर संग्रहित किए गए फ़ंक्शंस लागू होते हैं। जैसे list2 = list.apply (curriedFunction) जहां curriedFunction हो सकता है 2 * ?जावा में आप एक लूप के साथ ऐसा करेंगे।
पीटर लॉरी

@ पेटर: यह आंशिक अनुप्रयोग है, करीने से नहीं। और न ही संग्रह संचालन के लिए विशिष्ट है।
लापता

@missingfaktor, मेरी बात है; एक विशिष्ट सुविधा पर लटका हुआ नहीं है, लेकिन एक कदम पीछे ले जाएं और व्यापक समस्या को देखें और एक सरल समाधान होने की संभावना है।
पीटर लॉरी

@Peter: यदि आप प्रश्न के बिंदु पर सवाल करना चाहते हैं, तो आपको अपनी टिप्पणी एक टिप्पणी के रूप में पोस्ट करनी चाहिए, न कि उत्तर के रूप में। (IMHO)
11:53

2

यह जावा में करी और आंशिक अनुप्रयोग के लिए एक पुस्तकालय है:

https://github.com/Ahmed-Adel-Ismail/J-Curry

यह विनाशकारी टुपल्स और मैप का समर्थन भी करता है। विधि मापदंडों में विभाजित करें, उदाहरण के लिए Map.Entry पास करने के लिए एक विधि जो 2 पैरामीटर लेती है, इसलिए Entry.getKey () पहले पैरामीटर पर जाएगी, और Entry.getValue () दूसरे पैरामीटर के लिए जाएगा

README फ़ाइल में अधिक जानकारी


2

Java 8 में Currying का उपयोग करने का लाभ यह है कि यह आपको उच्च क्रम के कार्यों को परिभाषित करने देता है और फिर एक जंजीरदार, सुरुचिपूर्ण तरीके से पहले ऑर्डर फ़ंक्शन और फ़ंक्शन तर्क पास करता है।

यहाँ पथरी के लिए एक उदाहरण है, व्युत्पन्न कार्य।

  1. व्युत्पन्न फ़ंक्शन सन्निकटन को (f (x + h) -f (x)) / h के रूप में परिभाषित करता है । यह हाई ऑर्डर फंक्शन होगा
  2. आइए 2 अलग-अलग कार्यों के व्युत्पन्न की गणना करें, 1 / x , और मानकीकृत गाऊसी वितरण

1

    package math;

    import static java.lang.Math.*;
    import java.util.Optional;
    import java.util.function.*;

    public class UnivarDerivative
    {
      interface Approximation extends Function<Function<Double,Double>, 
      Function<Double,UnaryOperator<Double>>> {}
      public static void main(String[] args)
      {
        Approximation derivative = f->h->x->(f.apply(x+h)-f.apply(x))/h;
        double h=0.00001f;
        Optional<Double> d1=Optional.of(derivative.apply(x->1/x).apply(h).apply(1.0)); 
        Optional<Double> d2=Optional.of(
        derivative.apply(x->(1/sqrt(2*PI))*exp(-0.5*pow(x,2))).apply(h).apply(-0.00001));
        d1.ifPresent(System.out::println); //prints -0.9999900000988401
        d2.ifPresent(System.out::println); //prints 1.994710003159016E-6
      }
    }

0

हां, मैं @ Jérôme से सहमत हूं, जावा 8 में वक्रिंग स्केल या अन्य प्रोग्रामिंग प्रोग्रामिंग भाषाओं की तरह मानक तरीके से समर्थित नहीं है।

public final class Currying {
  private static final Function<String, Consumer<String>> MAILER = (String ipAddress) -> (String message) -> {
    System.out.println(message + ":" + ipAddress );
  };
  //Currying
  private static final Consumer<String> LOCAL_MAILER =  MAILER.apply("127.0.0.1");

  public static void main(String[] args) {
      MAILER.apply("127.1.1.2").accept("Hello !!!!");
      LOCAL_MAILER.accept("Hello");
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.