基本函式式短碼

趙年峰發表於2013-01-28

這裡會有一些基本的函式式短碼的例子

Java趣味短碼

Java趣味短碼 - (第一節)

今天跟公司的童鞋聊天的時候,談到了關於短碼和程式碼的精簡的方式,所以整理出來。

需求很簡單。

首先定義一個類

class Item{
    public int key;
    public int l;
    public int r;
};

然後主函式的場景大概是這樣

public static void main(String[] args) {
    Item x;
    x = new Item();
    x.key = 1;
    x.l = 10;
    x.r = 20;

    int i = 0;

    if (x.key > i){
        i = x.l;
    }else{
        i = x.r;
    }

    i = 0;
    if ( x.key > i){
        x.l = i;
    }else{
        x.r = i;
    }
}

這裡面有兩個子場景,就是接下來要討論的。

子場景1

    if (x.key > i){
        i = x.l;
    }else{
        i = x.r;
    }

子場景2

    if ( x.key > i){
        x.l = i;
    }else{
        x.r = i;
    }
  • 子場景1 的規律是 左面的值都是一樣的,都是賦值給i

  • 子場景2 的規律是 右面的值都是一樣的,都是用i賦給別的變數。

那麼我們如何來簡化實現這兩類場景呢?

第一個場景很簡單,可以如下優化:

i = ( x.key >i ? x.l : x.r);

第二個場景比較棘手!

因為表示式不能被賦值。

所以我們增加一個函式:

public static  boolean to_(Object s ){
    return true;
}

有了如上函式我們就可以這樣寫

( x.key >i ? to_( x.l = i ): to_(x.r = i) )

這裡的含義就是先複製給x.l或者x.r根據x.key>i的真假值。然後傳給to_函式,至於to_不會考慮傳過來的值是多少。反正x.key>i都會判斷。直接暴力返回true。這樣就完美的解決了左賦值的問題。也解決了Integer和int無法passbyValue的問題。

如下是完整的程式碼。

package tPackge;


class Item{
    public Integer key;
    public Integer l;
    public Integer r;
};

public class test01 {
    public static  boolean to_(Object s ){
        return true;
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        Item x;
        x = new Item();
        x.key = 1;
        x.l = 10;
        x.r = 20;

        Integer i = 0;

        if (x.key > i){
            i = x.l;
        }else{
            i = x.r;
        }
        System.out.println(x.l);
        System.out.println(x.r);
        System.out.println(i);
        System.out.println("--------------------------");        
        i = ( x.key >i ? x.l : x.r);
        System.out.println(x.l);
        System.out.println(x.r);
        System.out.println(i);
        /*
        if ( x.key > i){
            x.l = i;
        }else{
            x.r = i;
        }
        */
        System.out.println("--------------------------");
        i = 0;
        //if ( x.key > i ) { x.l = i; } else { x.r = i; } 

        //if ( ( x.key >i ? ( ( x.l = i ) == i ): ( (x.r = i) == i ) ) ){ System.out.println(i); }
        if ( ( x.key >i ? to_( x.l = i ): to_ (x.r = i) ) ){ System.out.println(i); }
        System.out.println(x.l);
        System.out.println(x.r);
    }

}

Java趣味短碼 - 能像 python的 enumerate一樣嗎?

Java趣味短碼 - 能像 python的 enumerate一樣嗎?

昨天和童鞋討論過關於IF,然後童鞋的朋友是個pythoner,也在一起討論,當時提出一個問題,java沒有像python裡的enumerate這個東西吧?其實java裡沒有的東西還慢多的。不過我們可以讓他有。

下面是python的程式碼

for index,var in enumerate(a):
    tl = tl + [ var-n+index , var , var+n-index ]

enumerate(a)

假設

a = ["test","hello","test"]

能根據a的結構生成如下結構:

 a = (0,"test"),(1,"hello"),(2,"test")

在java中如何實現呢?

如果你去搜尋stackoverflow 找到這樣一篇文章,戳進

裡面的方法是這樣

List<String> numbers = Arrays.asList("zero", "one", "two");
ListIterator<String> it = numbers.listIterator();
while (it.hasNext()) {
    System.out.println(it.nextIndex() + " " + it.next());
}

如上的方法雖然實現了基本需求,但不美觀。

那麼我們怎麼實現呢?

思路如下:

  • 實現迭代器
  • 建立一個替代物件,增加一個索引和一個物件。

首先定義一個迭代物件

@SuppressWarnings("unused")
class Enum{    
    public int I;
    public Object Obj;
    public Enum( Object Obj , Integer I){
        this.I = I;
        this.Obj = Obj;
    }
}

然後我們定義enumerate物件

@SuppressWarnings("unchecked")
class Enumerate<T>  implements Iterator<Enum> , Iterable<Enum>{
    private Iterator it ;
    private int current;

    public Enumerate(Collection<T> t){
        it = t.iterator();
    }

    @Override
    public boolean hasNext() {
        return it.hasNext();
    }

    @Override
    public Enum next() {
        return new Enum(it.next(),++current);
    }

    @Override
    public void remove() {
        it.remove();

    }

    @Override
    public Iterator<Enum> iterator() {
        return this;
    }
}

這個物件我們增加了一個current計數器。用來返回index的

這裡要說明為什麼不用stackoverflow帖子中的方法,缺點就是當list或者set這種集合過大的時候。那麼你的演算法週期就要*2。這是很難讓人接受的事情。

所以使用迭代的話,會逐條記錄返回,這樣不會改變原來的程式碼時間。

SO!我們可以看到如下程式碼了。

for ( Enum a : new Enumerate<Integer>(l)){
    System.out.println(a.Obj);
    System.out.println(a.I);
}

是不是感覺很酷!

下面的程式碼是一份完整的程式碼樣例。

package tPackge;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;


@SuppressWarnings("unused")
class Enum{    
    public int I;
    public Object Obj;
    public Enum( Object Obj , Integer I){
        this.I = I;
        this.Obj = Obj;
    }
}

@SuppressWarnings("unchecked")
class Enumerate<T>  implements Iterator<Enum> , Iterable<Enum>{
    private Iterator it ;
    private int current;

    public Enumerate(Collection<T> t){
        it = t.iterator();
    }

    @Override
    public boolean hasNext() {
        return it.hasNext();
    }

    @Override
    public Enum next() {
        return new Enum(it.next(),++current);
    }

    @Override
    public void remove() {
        it.remove();

    }

    @Override
    public Iterator<Enum> iterator() {
        return this;
    }
}

public class test02 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List <Integer>  l = Arrays.asList(1,2,3,4,5,5);

        int i = 0;
        for ( Integer a : l){
            System.out.println(i);
            System.out.println(a);
            i++;
        }

        for ( Enum a : new Enumerate<Integer>(l)){
            System.out.println(a.Obj);
            System.out.println(a.I);
        }
    }

}

java趣味短碼-for的邏輯操作對比

java趣味短碼-for的邏輯操作對比

前面介紹了三元表示式和三元表示式的用法,當然還有迭代器。下面就是一個比較特殊的問題,for的寫法長度比較和優劣。當然這也是一個和童鞋討論思考了很久的問題。

首先說明一下里面的做法參考的stackoverflow中的一篇討論貼子中的關於each的做法

首先我們準備一下相關函式

建立一個介面,用來實現each操作用的

interface Function<T>{
    public void func(T Item);
}

實現一個each函式。

@SuppressWarnings("unchecked")
public static <T> void each(Collection<T> s, Function f){
    for ( T a : s){
        f.func(a);
    }
}

實現一個集合增加元素的三元表示式邏輯操作函式。

public static <T> int add_(Collection<T> s, T item){
    s.add(item);
    return 0;

}

我們的需求如下:

需求1:在指定的List中去掉小於等於2的元素,大於2的元素放到目標列表d中,並列印d中所有的元素,我們只針對過濾元素的程式部分進行優化。

List <Integer> l = Arrays.asList(1,2,3,4,5,6,7);

final List <Integer> d = new ArrayList<Integer>();

for ( Integer a : l){
    if (a > 2){ d.add(a); }
}

我們需要優化的程式碼如下:

for ( Integer a : l){
    if (a > 2){ d.add(a); }
}

下面我們看看,有2種寫法:

//first
test03.each(l, new Function<Integer>(){    
    @Override
    public void func(Integer Item) { if ( Item > 2) { d.add(Item); } }
});

優點:去掉了for,把他封裝到了函式裡,更專注元素的處理,且邏輯更鮮明,程式碼行數的可預料性大。

缺點:可擴充套件的難度十分大,比如需要2個元素對比等,需要開發不同的需求的each版本,不過實際上這個缺點也算是優點,因為會使程式碼規範化,程式更能公式化。

for ( Integer a : l ){ int b = ( a >2 ? add_(d,a) : 0  ) ; }

優點:僅僅從程式碼行數和程式碼文字列數來說,這樣寫確實精簡了。

缺點:幾乎不適合作為client-code的典範,不推薦這樣寫。

總結: 從上面看,如果剛開始就需要對程式碼進行統計和規範化建議first寫法,並且可以開發出來一套這樣的函式式框架提供給程式設計師,程式碼的統計和程式碼的規範化更易於預測和思考(因為基本都可以通過公式化的開銷來計算)

需求2:在指定的List中 小於等於2的元素增加1並存放在目標列表中,大於2的元素直接放到目標列表中。

原始碼如下:

for ( Integer a : l){ if (a > 2){ d.add(a); } else { d.add( a + 1); } }

寫法 1

//first
test03.each(l, new Function<Integer>(){    
    @Override
    public void func(Integer I) {
        if ( I > 2) { d.add(I); } else { d.add(I+1); }
    }
});

優點:同上。

缺點:同上。

補充 : 這時候已經能夠凸顯固定模式函數語言程式設計的特點了。

寫法2

//second
for ( Integer a : l ){ int b=( a >2 ? add_(d,a) : add_(d,a+1) ); }

優點:比標準寫法短了不少!。

缺點:對更多的if elseif else這樣的結構,這種寫法直接就被pass了,所以經證明這種寫法在if else這樣的條件結構的時候,還蠻不錯。

總結:

需求1,不優化是最簡潔的,但第1種優化的擴充套件和模式化程式設計較好。

需求2,第2種優化最短,可讀性也不錯,但擴充套件性為0

結論:第一種優化確實不錯的,在Java中android平臺被這種寫法是被廣泛應用。

相關文章