May 25, 2013

Project Lambda ၏ API အသစ်များ

ပြီးခဲ့သော ဘလောဂ်ဖြင့် Project Lambda ၏ Lambda နည်း၏ ဖော်ပြပုံတို့ကို ဖော်ပြခဲ့၏။ ဤတစ်ခေါက်တွင် Lambda နည်း အားအသုံးပြုနိုင်သော API များနှင့် ပတ်သက်ပြီး ဖော်ပြသွားပါဦးမည်။

အရင်တစ်ခေါက်တွင်ဖော်ပြခဲ့သလိုပင် Project Lambda ၏ ရည်ရွယ်ချက်မှာ Multi Core ပတ်ဝင်းကျင်တွင် အသုံးပြုသော Program များအား Support လုပ်ရန်ပင်ဖြစ်၏။ အထူးသဖြင့် Multi Core ခေတ်၏ Core များအား အား၍မနေရစေရန် Parallel Process များဖြင့် အလုပ်လုပ်စေရန် ပင်ဖြစ်၏။ အထူးသဖြင့် သေးငယ်သော Task များအား အပြိုင်အလုပ်လုပ်စေရန် ရည်ရွယ်ပါသည်။

Java ဘာသာရပ်တွင် Parallel Process အား အသုံးပြုရာတွင် ယခင်က Thread များအား အသုံးပြု၍၎င်း၊ နောက်ပိုင်းတွင် ကြီးမားသော Task များဆိုပါက Concurrency Utilities ၏ ExecutorService အား အသုံးပြု၍၎င်း၊ သေးငယ်သော Task များဆိုပါက Fork / Join အား အသုံးပြု၍ ဖြေရှင်းခဲ့ကြပါသည်။ သို့ရာတွင် Fork / Join ၏ အသုံးပြုပုံမှာ အနည်းငယ်ရှုပ်ထွေး၍၊ အသုံးပြုပုံနေရာမှာလည်း အကန့်အသန့်မှာ ရှိနေခဲ့ပါသည်။

ဤနေရာတွင် အာရုံစိုက်စရာဖြစ်လာသည်မှာ Iterator များဖြစ်၏။ Iterator များအား Parallel Process များတွင် အသုံးများလာသည်ကို တွေ့ရပါသည်။ ဥပမာအားဖြင့် OpenMP တွင် Loop များအား တမင်တကာပင် Parallel Process ဖြင့် အသုံးပြုရန် ရေးသားထားသည်ကို တွေ့ရ၏။ Project Lambda တွင်လည်း Fork / Join Framework အား အသုံးပြု၍ Iterator အား Parallel Process တွင် အသုံးပြုနိုင်ရန် API များအား ပြင်ဆင်လာခဲ့ပါသည်။

Java ၏ For ဝါကျအသစ် တွင်အသုံးပြုသော Iterator အား External Iterator ဟု ခေါ်ဆိုပြီး၊ Program ဘက်တွင် Iteration အား Control လုပ်ရန် လိုအပ်ပါသည်။ ဤကဲ့သို့ရေးသားရာတွင် Iteration လုပ်သည့် Process များအား Task တစ်ခုအနေဖြင့် သီးသန့် ရေးသားရန်မှာ လွန်စွာခက်ခဲလှပေသည်။

ယခုတစ်ခေါက် Project Lambda တွင် အသုံးပြုသော Iterator မှာ Internal Iterator ဖြစ်ပါသည်။ Internal Iterator တွင် Collection ဘက်တွင် Iteration အား Control လုပ်လာပါသည်။ ဤကဲ့သို့သော Internal Iterator များအား Script Language နှင့် Functional Language များတွင် အသုံးများသည်ကို တွေ့ရပါသည်။ ဥပမာအားဖြင့် Groovy ဘာသာရပ်တွင် Collection အတွင်းရှိ Elements များအား အသီးသီး Print လုပ်ရန် အောက်ပါအတိုင်း ရေးသားသည်ကို တွေ့ရပါလိမ့်မည်။
def list= ...
list.each{ it -> println it };
ဤနေရာတွင် each method ၏ Argument အနေဖြင့် အသုံးပြုနေသည်မှာ Closure ပင်ဖြစ်၏။ it နေရာတွင် Collection ၏ Elements များ တစ်ခုချင်းစီ ဝင်ရောက် အစားထိုးခြင်းအားဖြင့် ၎င်း Elements များအား Print လုပ်နိုင်ခြင်း ပင်ဖြစ်၏။ Groovy ဘာသာရပ်တွင် Task တစ်ခုအား Closure အား အသုံးပြု၍ ဖော်ပြသော်လည်း၊ Java ဘာသာရပ်တွင် Lambda ရေးနည်းအား အသုံးပြု၍ ဖော်ပြခြင်းသာ ဖြစ်၏။

Project Lambda တွင် Iterable Interface အား ပြုပြင်ကာ Internal Iterator အား ရေးသားနိုင်ရန် ပြင်ဆင်လာခဲ့ပါသည်။ ထို့အပြင် Stream ဟုခေါ်သော Collection တစ်ခုအားလည်း ဖြည့်စွက်လာခဲ့ပါသည်။ အဆိုပါ Stream အား အသုံးပြု၍ Parallel Process များအား ရေးသားနိုင်ရန် ပြုပြင်လာခဲ့ခြင်းပင် ဖြစ်ပါသည်။


Iterable Interface ၏ ပြုပြင်ပြောင်းလည်းမှု့များ


java.lang.Iterable Interface သည် For ဝါကျအသစ်တွင် Iterator အား အသုံးပြုနိုင်သည် ဆိုသည်ကို ဖော်ပြပေးသော Interface တစ်ခုပင်ဖြစ်၏။ ဤ Iterator သည် External Iterator တစ်ခု ပင်ဖြစ်၏။

၎င်းအစား Project Lambda တွင် Iterable Interface ဖြင့် Internal Iterator အား အသုံးပြုနိုင်စေရန် forEach method ကို အသစ်ဖြည့်စွက်လာခဲ့ပါသည်။ အဆိုပါ forEach Method ၏ Argument Type သည် Functional Interface ဖြစ်သော Consumer Interface ပင် ဖြစ်၏။ ဥပမာအားဖြင့် အထက်ဖော်ပြခဲ့သော Groovy ၏ ရေးသားပုံအား forEach Method ကို အသုံးပြု၍ ရေးသားမည် ဆိုပါက အောက်ပါအတိုင်း ရေးသားရမည် ဖြစ်ပါသည်။
List&ltstring&gt list = ...
list.forEach( it -> System.out.println(it));
forEach Method ၏ Argument နေရာတွင် ရေးသားထားသည်မှာ Consumer Interface ၏ Anonymous Class အား ဖော်ပြနေသား Lambda နည်း ပင် ဖြစ်၏။ Groovy တွင် ရေးသားထား သကဲ့သို့ပင် it ၏ နေရာတွင် Collection ၏ Element တစ်ခုချင်း ဝင်ရောက် အစားထိုးမည် ဖြစ်သောကြောင့် ၎င်းအား ပြန်လည် Print Out လုပ်နိုင်ခြင်း ဖြစ်ပါသည်။ ဤနည်းအားဖြင့် Iterable Interface အား အသုံးပြု၍ Internal Iterator အား အသုံးပြုနိုင်မည် ဖြစ်ပါသည်။

သို့ရာတွင် ဤကဲ့သို့ Interface တွင် Method တစ်ခုအား ဖြည့်စွက်မည် ဆိုပါက၊ ၎င်း Interface အား Implement ပြုလုပ်ထားသော Class များအားလုံးအား ပြုပြင်ရေးသားရန် လိုအပ်လာပါလိမ့်မည်။ ထို့ကြောင့် ရှိပြီးသား Class များအား အကျိုးသက်ရောက်မှု့ နည်းပါးစေရန် အတွက် Default ရေးနည်း အား ဖြည့်စွက်လာခဲ့ကြခြင်း ဖြစ်ပါသည်။


Interface ၏ Default Method ရေးနည်း


Interface ၏ Default Method အား ရေးသားရာတွင် Method ၌ default စာလုံးအား ဖြည့်စွက်၍ ရေးသားနိုင်ပါသည်။ ဥပမာအားဖြင့် Iterable Interface ၏ Default Method အား ရေးသားရာတွင် အောက်ပါအတိုင်း ရေးသားနိုင်ပါသည်။
@FunctionalInterface
public interface Iterable&ltT&gt {
    Iterator&ltT&gt iterator();

    public default void forEach(Consumer consumer) {
        for (T t : this) {
            consumer.accept(t);
        }
    }
}
အထက်ဖော်ပြပါအတိုင်း default စာလုံးအား၍ Interface ၏ Default Method အား ရေးသားနိုင်ပါသည်။ အဆိုပါ Interface အား Implements လုပ်ထားသော Class အတွင်းတွင် ၎င်း Method အား ဖြည့်စွက်ရေးသားထားခြင်း မရှိပါက Default Method ကို အသုံးပြုသွားမည် ဖြစ်သည်။ အကယ်၍ Implement လုပ်ထားသော Class အတွင်းတွင် ၎င်း Method အား Override လုပ်၍ ရေးသားမည်ဆိုလည်း ရေးသားနိုင်ပါသည်။ ထိုအခါမျိုးတွင် Override လုပ်ထားသော Method အား အသုံးပြုသွားမည် ဖြစ်ပါသည်။
တစ်ခု သတိထားရန်မှာ ဤကဲ့သို့ Interface ၏ ဖွဲ့စည်းပုံကို ပြောင်းလည်းခြင်း အားဖြင့် လက်တွေ့အရ Java ဘာသာရပ်တွင် Multi Inheritance လုပ်သွားသလို ဖြစ်မည်ဆိုသည့် အချက်ပင်ဖြစ်သည်။ ယနေ့တိုင် Java ဘာသာရပ်၏ Inheritance သည် Single Inheritance ဖြစ်သောကြောင့် Default Method အား ရေးသားရာတွင် ထိုအချက်ကို ဂရုပြု၍ ရေးသားရန် လိုအပ်ပါသည်။


java.util.stream.Stream


java.util.stream.Stream ဆိုသည်မှာ အဆုံးပိုင်းကိုသတ်မှတ်ထားခြင်း မရှိသော Collection တစ်မျိုးဖြစ်ပြီး၊ အဆုံးမရှိသော List တစ်မျိုးဟု မှတ်ယူနိုင်ပါသည်။ အထက်ဖော်ပြပါ Stream သည် Stream Interface အား ရည်ညွှန်းပြီး၊ ၎င်းတွင် filter, map နှင့် reduce method များကို သတ်မှတ်ထားပါသည်။ အဆိုပါ Method များအားလုံး၏ Argument သည် Functional Interface ဖြစ်ပြီး Lambda ရေးနည်းအား အသုံးပြုနိုင်ပါသည်။

ဥပမာအနေဖြင့် Integer များပါဝင်သော List အတွင်းမှ စုံကိန်းများ၏ ပေါင်းလဒ်ကို တွက်ချက်ကြည့်သော ပရိုဂရမ်အား ရေးသားကြည့်ပါမည်။ Java 8 မပေါ်မှီက ဆိုပါက အောက်ပါအတိုင်းရေးသားရမည် ထင်ပါသည်။
import java.util.List;
import java.util.ArrayList;

public class NoLambda {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for(int i=0; i < 1000;i++)
            list.add(i);
        
        Integer result = 0;

        for (Integer i : list) {
            if(0 == i % 2)
                result += i;
        }
        
        System.out.println(result);
    }
}
Java 8 ၏ Stream နှင့် Lambda ရေးနည်းအား အသုံးပြု၍ ရေးသားကြည့်ပါမည်။
import java.util.List;
import java.util.ArrayList;

public class LambdaStream {
    public static void main(String ... args) {
        List<Integer> list = new ArrayList<>();
        for (int i=0; i < 1000; i++)
            list.add(i);
        
        int result = list.stream().filter(x-> 0 == x%2)
            .reduce(0, (s, x) -> s+x);
        
        System.out.println(result);
    }
}
အထက်ပါ ကုဒ်များ၏ စာကြောင်း ၁၀တွင် List#stream Method အား အသုံးပြု၍ List မှ Stream ကို ပြောင်းယူပါသည်။ တဖန် Stream#filter အား အသုံးပြု၍ 2 နှင့် စား၍ပြတ်သော Element ကို Filter လုပ်ပါသည်။ နောက်ဆုံးတွင် Stream#reduce အား အသုံးပြု၍ Filter လုပ်၍ ရွေးချယ်ထားသော Element တစ်ခုစီအား ပေါင်းစေ၍ နောက်ဆုံးတွင် ရလဒ်အား Return လုပ်လိုက်ပါသည်။

ဤရေနည်းသည် Functional Language များတွင် အသုံးများသော ရေးနည်းဖြစ်ပါသည်။ ဤနေရာတွင် OOP ဘာသာရပ်တစ်ခုဖြစ်သော Java အား အသုံးပြုနေလင့်ကစား Functional Language ၏ အတွေးအခေါ်များအား ဖြည့်စွက်ကာ အသုံးပြုရန် လိုအပ်ပါသည်။

သို့ရာတွင် Java 8 မတိုင်ခင်က ရေးထားသော ကုဒ်များနှင့် စာလျှင် အများကြီး ရေးရသက်သာလာသည်ကို တွေ့ရပါသည်။


Parallel Processing


အထက်တွင်ဖော်ပြခဲ့သော Stream အား အသုံးပြုနေသော အလုပ်လုပ်ပုံသည် အစီအစဉ်အလိုက် အလုပ်လုပ်ခိုင်းနေသည်ကို တွေ့ရပါသည်။ Sequential Processing ဖြစ်၏။ Project Lambda ၏ အဓိက ရည်ရွယ်ချက်သည် Parallel Processing အတွက်ရည်ရွယ်ထားသည်ဟု ယခင်တစ်ခေါက် ဘလောဂ်တွင် ဖော်ပြခဲ့ဘူးပါသည်။ အထက်ပါ နမှုနာအား Parallel Processing ဖြင့် Stream အား အသုံးပြုရန် ရေးသားကြည့်ပါမည်။
import java.util.List;
import java.util.ArrayList;

public class LambdaStream {
    public static void main(String ... args) {
        List&ltinteger&gt list = new ArrayList<>();
        for (int i=0; i < 1000; i++)
            list.add(i);
        
        int result = list.parallelStream().filter(x-> 0 == x%2)
            .reduce(0, (s, x) -> s+x);
        
        System.out.println(result);
    }
}
စာကြောင်း ၁၀ အားကြည့်ပါ။ List#stream အစား List#parallelStream အား အသုံးပြုလိုက်ရုံဖြင့် Parallel Processing အား အသုံးပြုနိုင်သော Stream အား ရရှိနိုင်သည်ကို တွေ့ရပါလိမ့်မည်။

လေ့လာခဲ့ရာတွင် Project Lambda တွင် Lambda ရေးသားနည်းမှ အစပြု၍၊ Interface ၏ Default ရေးသားခြင်း၊ Stream အစရှိသည်တို့ကို ပံ့ပိုးလာခဲ့သည်ကို တွေ့ရပါသည်။ Lambda ရေးသားနည်းဖြင့် Task များအား ရှင်းလင်းစွာရေးသားလာနိုင်သည်ကို တွေ့ရပြီး၊ Stream အား အသုံးပြုခြင်း အားဖြင့် Collection များအား Parallel Processing များတွင် လွယ်ကူစွာ အသုံးပြုလာနိုင်ပါသည်။

တဖန် Stream ကြောင့်ပင် Collection ၏ Process များ ရေးသားနည်းမှာ အတော်ပင်ပြောင်းလည်း သွားပြီး၊ Functional Language များ၏ Concept များအား ဖြည့်စွက်လာသည်ကို တွေ့ရပါသည်။ ထို့ကြောင့် Project Lambda ၏ Function များအား အပြည့်အဝ အသုံးပြုနိုင်ရန် Functional Language များ၏ Concept များအား နားလည်ထားရန် လိုအပ်ပါသည်။

နောက်အခန်းများတွင် Swing ၏ နေရာတွင် အစားထိုး ဝင်ရောက်လာခဲ့သော Java ၏ GUI Framework အသစ်တစ်ခုဖြစ်သော JavaFX အကြောင်းကို ဆက်လက်၍ ဖော်ပြသွားပါဦးမည်။

ဤနေရာမှ JDK 8 Early Access အား ရယူ၍ နမှုနာကုဒ်များအား ကွန်ပိုင်းလုပ်ကာ စမ်းသပ်နိုင်ပါသည်။

ကိုးကား
http://www.computerworld.jp/topics/3366/206732?page=0,0
http://download.java.net/lambda/b78/docs/api/java/util/List.html
http://download.java.net/lambda/b78/docs/api/java/util/stream/Stream.html

လေးစားစွာဖြင့်
မင်းလွင်

No comments:

Post a Comment