Bunu Java'da çekmenin bir yolu olup olmadığını merak ediyordum. Kapanışlar için yerel destek olmadan bunun mümkün olmadığını düşünüyorum.
Bunu Java'da çekmenin bir yolu olup olmadığını merak ediyordum. Kapanışlar için yerel destek olmadan bunun mümkün olmadığını düşünüyorum.
Yanıtlar:
Java 8 (18 Mart 2014'te piyasaya sürüldü) körlemeyi destekliyor. Missfaktor tarafından cevaba gönderilen örnek Java kodu şu şekilde yeniden yazılabilir:
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));
}
}
... ki bu oldukça hoş. Kişisel olarak, Java 8 ile Scala veya Clojure gibi alternatif bir JVM dili kullanmak için çok az neden görüyorum. Elbette başka dil özellikleri sağlarlar, ancak bu, geçiş maliyetini ve daha zayıf IDE / araç / kitaplık desteği, IMO'yu haklı çıkarmak için yeterli değildir.
(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)
Java'da curing ve kısmi uygulama kesinlikle mümkündür, ancak gereken kod miktarı muhtemelen sizi kapatacaktır.
Java'da körili ve kısmi uygulamayı gösteren bazı kodlar:
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, yukarıdaki Java kodunun Haskell eşdeğeridir:
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
Java 8 ile Currying için birçok seçenek vardır. Her ikisi de kutudan Currying'i sunan Javaslang ve jOOλ işlev türü (bunun JDK'da bir gözetim olduğunu düşünüyorum) ve Cyclops İşlevleri modülünde Currying JDK İşlevleri için bir dizi statik yöntem vardır ve yöntem referansları. Örneğin
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;
}
Tüketiciler için "Currying" de mevcuttur. Örneğin, 3 parametresi olan bir yöntemi döndürmek için ve önceden uygulanmış olanlardan 2'si buna benzer bir şey yaparız
return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
currying
de Curry.curryn
kaynak kodu.
DÜZENLEME : 2014 ve Java 8 itibariyle, Java'da işlevsel programlama artık sadece mümkün değil, aynı zamanda çirkin de değil (güzel demeye cesaret ediyorum). Örneğin Rogerio'nun cevabına bakınız .
Eski cevap:
Fonksiyonel programlama tekniklerini kullanacaksanız, Java en iyi seçenek değildir. Missfaktor'un yazdığı gibi, istediğinizi elde etmek için oldukça büyük miktarda kod yazmanız gerekecek.
Öte yandan, JVM'de Java ile sınırlı değilsiniz - işlevsel diller olan Scala veya Clojure'u kullanabilirsiniz (Scala aslında hem işlevsel hem de OO'dur).
Currying , bir işlev döndürmeyi gerektirir . Java ile bu mümkün değildir (işlev işaretçileri yoktur) ancak bir işlev yöntemi içeren bir türü tanımlayabilir ve döndürebiliriz:
public interface Function<X,Z> { // intention: f(X) -> Z
public Z f(X x);
}
Şimdi let köri basit bölme. Bir Bölücüye ihtiyacımız var :
// 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;
}
}
ve bir DivideFunction :
// f(x) -> g
public class DivideFunction implements Function<Double, Function<Double, Double>> {
@Override
public function<Double, Double> f(Double x) {
return new Divider(x);
}
Şimdi körili bir bölme yapabiliriz:
DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.); // calculates f(1,2) = 0.5
Pekala, Scala , Clojure veya Haskell (veya başka herhangi bir işlevsel programlama dili ...) kesinlikle curry ve diğer işlevsel hileler için kullanılacak dillerdir .
Bunu söyleyince, Java ile beklenebilecek süper miktarlarda standart şablon olmadan curry yapmak kesinlikle mümkündür (pekala, türler konusunda açık olmak çok acı verir - sadece curried
örneğe bir göz atın ;-)).
Testler, hem vitrin feryat currying bir Function3
içine 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);
}
Bu örnekte gerçekten tip güvenli olmasa da, kısmi uygulamanın yanı sıra :
@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);
}
Bu, JavaOne'dan önce yarın "sıkıldığım için" ;-) eğlence için uyguladığım bir Proof of Concept'ten alınmıştır ;-) Koda buradan ulaşabilirsiniz: https://github.com/ktoso/jcurry
Genel fikir, nispeten kolay bir şekilde FunctionN => FunctionM'ye genişletilebilir, ancak "gerçek tip güvenlik" partia uygulama örneği için bir sorun olmaya devam ediyor ve currying örneği , jcurry'de çok fazla kazanplaty koduna ihtiyaç duyacak , ancak yapılabilir.
Sonuç olarak, yapılabilir, ancak Scala'da kutunun dışında ;-)
Java 7 MethodHandles ile körleme taklit edilebilir: 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
}
}
Evet, kendiniz için kod örneğine bakın:
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
}
}
Bu basit bir örnek curriedAdd başka bir işlevi döndüren bir curried fonksiyonudur, ve bunun için de kullanılabilir parametrelerin kısmi uygulama saklanan olarak curried kendi içinde bir fonksiyonu olan. Bu şimdi daha sonra ekrana yazdırdığımızda tam olarak uygulanıyor.
Dahası, daha sonra nasıl JS tarzında kullanabileceğinizi görebilirsiniz.
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to
curriedAdd(1)(2) // in JS
Java 8 olasılıklarını bir kez daha ele alın:
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
Function<Integer, Integer> increment = y -> add.apply(1, y);
assert increment.apply(5) == 6;
Ayrıca bunun gibi yardımcı yöntemler de tanımlayabilirsiniz:
static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
return a2 -> f.apply(a1, a2);
}
Bu size tartışmaya açık bir şekilde daha okunabilir bir sözdizimi verir:
Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;
Java'da bir yöntemi köreltmek her zaman mümkündür, ancak standart bir şekilde desteklemez. Bunu başarmaya çalışmak karmaşıktır ve kodu oldukça okunamaz hale getirir. Java bunun için uygun dil değil.
Java 6+ için başka bir seçenek daha var
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);
}
o zaman bu yolla körileştirebilirsin
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();
Currying'i Java'da yapabilseniz de çirkin (çünkü desteklenmiyor) Java'da düz döngüleri ve basit ifadeleri kullanmak daha basit ve daha hızlıdır. Köriyi nerede kullanacağınıza dair bir örnek yayınlarsanız, aynı şeyi yapan alternatifler önerebiliriz.
2 * ?
Java'da bunu bir döngü ile yaparsınız.
Bu, Java'da körleme ve kısmi uygulama için bir kitaplıktır:
https://github.com/Ahmed-Adel-Ismail/J-Curry
Ayrıca, Tuples ve Map.Entry yöntem parametrelerine, örneğin 2 parametre alan bir yönteme bir Map.Entry geçirmek gibi, Entry.getKey () ilk parametreye ve Entry.getValue () öğeye gidecektir. ikinci parametre için gidecek
README dosyasında daha fazla ayrıntı
Currying'i Java 8'de kullanmanın avantajı, yüksek dereceli işlevleri tanımlamanıza ve ardından birinci dereceden bir işlevi ve işlev bağımsız değişkenlerini zincirleme, zarif bir şekilde iletmenize olanak vermesidir.
İşte Calculus için bir türev fonksiyonu örneği.
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
}
}
Evet, @ Jérôme ile aynı fikirdeyim, Java 8'de akma Scala veya diğer işlevsel programlama dillerinde olduğu gibi standart bir şekilde desteklenmiyor.
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");
}
}
Diğer tüm cevaplar belirli örneklere odaklanırken, ben yine de ikili fonksiyonları curried fonksiyonlara dönüştürmek için genel bir çözüm sağlamak istedim.
private static <A, B, C> Function<A, Function<B, C>> Curry(BiFunction<A, B, C> f) {
return a -> b -> f.apply(a, b);
}