青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

coreBugZJ

此 blog 已棄。

為什么我們要學習Haskell這樣的編程語言 (轉)

最近的幾個月,我一直在學習一種叫Haskell的編程語言。由于里面有太多的從未遇到的編程概念,整個過程就像是完全重新學習如何編程。在i.TV網站上,我寫了很多JavaScript(node.js和前端代碼)。雖然有不少的函數式/haskell式的編程模式不能引用進來,但仍有大量的技術思想讓我在使用javascript編程語言時受益不少。


你會發現Haskell庫里有能夠處理各種事情的各種各樣的函數。起初我以為這些只是一種技術上的積累,但隨后我認識到,這些函數相比起其它語言里的函數,它們能應用到形式更廣泛的問題中。這使得它們更有價值,因為我們都不太喜歡對一些常見的問題還不得不自己去寫解決方案。


這些函數是可以相互組合1的:它們能針對性的解決某些問題,而不對你的代碼做任何依賴,所以,你可以拼裝它們,組合成一個能夠解決你的大問題的東西。

 

高階函數(Higher Order Functions)

在Haskell語言中,最多的被反復使用的函數都是高階函數(higher order functions)——能以函數作為參數、能返回函數的函數。這使得它們具有固有的靈活性。下面是一個不太靈活的函數:它計算一個數組里等于某個值的元素的個數。

// 不靈活
function countMatching(array, value) {
    var counted = 0
    for (var i = 0; i < array.length; i++) {
        if (array[i] == value)
            counted++
    }
    return counted
}

// == 2
countMatching([1,3,3,4,5], 3)


它不靈活,因為它只能用來計算一個數組中精確匹配某個值的元素的個數。


下面是一個靈活一些的版本,它能接受一個函數,而不是一個值,作為參數。我們可以用它來對任何數據、任何對象進行比較。

// more flexible
function count(array, matching) {
    var counted = 0
    for (var i = 0; i < array.length; i++) {
        if (matching(array[i]))
            counted++
    }
    return counted
}

// == 2, same as first example
count([1,3,3,4,5], function(num) {
    return (num == 3)
})

// == 2, now we can use our functions for ANY kind of items or match test!
count([{name:"bob"}, {name:"henry"}, {name:"jon"}], function(obj) {
    return (obj.name.length < 4)
})


因為高階函數更具靈活性,你就更少有機會去寫它們,因為你一旦你寫成一個,你可以它應用到各種不同的情況中。

 

可重復利用的比較函數

你可能注意到了,count函數的寫法比countMatching更冗長。但是,雖然count函數可復用了,但比較函數2卻不可復用。如果是一些簡單的情況,這就足夠了,但經常,我們會需要更復雜的比較方法的函數。這樣的函數不僅僅可用于計數,它們可以用于任何事情上,一但你寫成或找到了這樣的函數,從長期的角度看,它們會節省你大量的時間和調試功夫。


讓我們來定義一個可復用的比較函數,達到我們的目的。==不是一個函數。我們是否可以定義一個eq函數來幫我們完成類似的事情呢?

function eq(a, b) {
    return (a == b)
}

count([1,3,3,4,5], function(num) {
    return eq(3, num)
})


我們向前邁進了一步:我們用了一個庫函數來完成比較任務,而不是使用我們現寫的代碼。如果eq函數很復雜,我們可以測試它并可以在其它的地方復用它。


但這使代碼變得冗長,因為count函數的參數是一個只需要一個參數——數組元素——的函數,而eq函數卻需要兩個參數。我還是要定義我們自己的匿名函數。讓我們來簡化一下這些代碼。


function makeEq(a) {
    // countMatchingWith wants a function that takes 
    // only 1 argument, just like the one we're returning
    return function(b) {
        return eq(a, b)
    }
}

// now it's only on one line!
count([1,3,3,4,5], makeEq(3))


我們寫了一個兼容count函數的函數(一個參數——數組元素——返回true或false)。看起來就像是count函數調用的是eq(3, item)。這叫做偏函數用法(partial function application)。

 

偏函數用法(Partial Application)

偏函數用法(Partial Function Application)是指創建一個調用另外一個部分參數已經預置的函數的函數的用法。這樣,它就能被別的地方,比如count函數,以更少的參數形式來調用。我們在makeEq函數里已經實現了這些,但是,我們并不想針對我們各種功能開發出各種版本的makeX(比如makeEqt,makeGt,makeLt等)函數。讓我們來找一種方法能通用于各種形式的函數。

function applyFirst(f, a) {
    return function(b) {
        return f.call(null, a, b)
    }
}

count([1,3,3,4,5], applyFirst(eq, 3))


現在我們不再需要一個makeEq函數了。任何2個參數的庫函數,我們都可以按這種方式調用。通過偏函數用法,使得定義即使是諸如==這樣簡單功能的各種函數都變得十分有意義,我們可以在高階函數中更容易的使用它們。


對那些超過2個參數的函數如何辦呢?下面的這一版本3能讓我們接受任意多的參數,高階函數可以自己追加參數。

function apply(f) {
    var args = Array.prototype.slice.call(arguments, 1)
    return function(x) {
        return f.apply(null, args.concat(x))
    }
}

function propertyEquals(propertyName, value, obj) {
    return (obj[propertyName] == value)
}

count([{name:"bob"},{name:"john"}], apply(propertyEquals, "name", "bob")) // == 1


我們預置了2個參數,“name” 和 “bob”,count函數補足了最后一個參數來完成整個調用。偏函數用法使我們能接受各樣的函數為參數,例如eq,然后把它們用于各樣的高階函數,例如count,以此來解決我們特定的問題。

 

配合ES5的 Map 和 Filter 功能函數的偏函數用法

ES5里有很多非常好的高階函數,underscore里的數量更多。讓我們看看filter函數——一個接收比較函數、過濾數組內容的函數。

// this equals [1,3,3]
[1,3,3,4,5].filter(function(num) {
    return (num < 4)
})


讓我們把它替換成一個可以復用的比較函數lt (less than)。

function lt(a, b) {
    return (a < b)
}

[1,3,3,4,5].filter(apply(lt, 4))


看上去添加這個lt函數的做法有點傻,但是,我們可以使用偏函數用法來創造一個很簡練的比較函數,當這個比較函數變的很復雜的時候,我們就能從對它的復用過程中獲得好處。


map
函數能讓你把數組里的一個東西變成另外一個東西。

var usersById = {"u1":{name:"bob"}, "u2":{name:"john"}}
var user = {name:"sean", friendIds: ["u1", "u2"]}

// == ["bob", "john"]
function friendsNames(usersById, user) {
    return user.friendIds.map(function(id) {
        return usersById[id].name
    })
}


我們寫一個可以復用的map變換函數,就像之前我們的可復用比較函數一樣。讓我們寫一個叫做lookup的函數。

function lookup(obj, key) {
    return obj[key]
}

// == [{name:"bob"}, {name:"john"}]
function friends(usersById, user) {
    return user.friendIds.map(apply(lookup, usersById))
}


很接近要求,但我們需要的是名稱,而不是friend對象本身。如果我們再寫一個參數顛倒過來的 lookup函數,通過第二次的map可以把它們的名稱取出來。

function lookupFlipped(key, obj) {
    return lookup(obj, key)
}

// == ["bob", "john"]
function friendsNames(usersById, user) {
    return friends(usersById, user)
            .map(apply(lookupFlipped, "name"))
}


但是我不想定義這個lookupFlipped函數,這樣干有點傻。這樣,我們來定義一個函數,它接收參數的順序是從右到左,而不是從左到右,于是我們就能夠復用lookup了。

function applyr(f) {
    var args = Array.prototype.slice.call(arguments, 1)
    return function(x) {
        return f.apply(null, [x].concat(args))
    }
}

// == ["bob", "john"]
function friendsNames(usersById, user) {
    return friends(usersById, user)
            .map(applyr(lookup, "name")) // we can use normal lookup!
}

applyr(lookup, "name")函數返回的函數只接受一個參數——那個對象——返回對象的名稱。我們不再需要反轉任何東西:我們可以按任何順序接受參數。


偏函數用法需要對一些常見的功能定義各種不同的函數,就像lt函數,但這正是我們的目的。你可以以偏函數用法把lt函數既用于count函數,也可用于Array.filter函數。它們可以復用,可以組合使用。


函數組合

在之前的例子中,我們遍歷了數組兩次,一次用來獲取users,一次為了獲取names。如果能在一次map映射操作中同時做這兩件事情,效率會高很多。

function friendsNames(usersById, user) {
    return user.friendIds.map(function(id) {
        var friend = lookup(usersById, id)
        return lookup(friend, "name")
    })
}


我們得到首次lookup的結果,把它第二次傳入lookup函數組合意思是串聯多個函數,組成一個新的函數,每一次串聯都是把前一個函數的輸出當作下一個函數的輸入。


讓我們來寫一個能這樣運轉的高階函數,利用它把friendsNames函數重寫成一個只需要單次map操作的函數。需要注意的是,函數串聯的執行順序是從右到左的,就跟你寫出f(g(x))這樣的代碼的運行方式一樣。

function compose(f, g) {
    return function(x) {
        return f(g(x))
    }
}

function friendsNames(usersById, user) {
    return user.friendIds.map(compose(applyr(lookup, "name"), apply(lookup, usersById)))
}


對數組的遍歷只進行了一次,只使用一次map操作,跟我們頭一個例子一樣。


我們不能使用我們寫出的friends函數,因為它既包含了如何取出一個friend的業務邏輯,也包含了map操作。friends函數是不能復用的,它的職責太多了——它是針對特定事物的。如果你們再寫一個friend函數,讓它只map一個friend,寫一個name函數,讓它返回對象的名稱呢?

var friend = lookup // lookup 恰巧能干我們想要的事情。
var name = applyr(lookup, "name")

function friendsNames(usersById, user) {
    // this line is now more semantic. 
    return user.friends.map(compose(name, apply(friend, usersById)))
}


相較于定義一個既包含轉換操作,又包含遍歷操作的friends函數,我們只定義了一個可做轉換操作的friend函數,而我們已經有了map函數為我做變換操作。friend函數比friends函數更具復用性,因為它包含更少的特定業務邏輯,能在更多的情形中使用。


這里你能找到更多的關于JavaScript里函數組合的信息。

 

函數式和功能單一化讓你的代碼庫更整潔

我發現我的很多的JavaScript代碼都是從無到有自己寫出來的。這不僅僅是說比起使用現成的程序包要效率低,它還會暗藏更多的bug,更難閱讀和維護。使用高階函數和偏函數用法,我們可以寫出可復用的程序庫,每個函數都精準的對應解決它們能解決的一部分問題。


隨著時間的推移,項目會變得越來越復雜,各部分越來越耦合,如果我們擁有的是一個能夠各自獨立測試不依賴的程序庫,我們的項目會從中受益,變得更健康,更穩定。

  1. 一種寬泛的組合。并不特指函數或對象組合,只是一種你用小東西組建大東西的思想。

  2. “Matching functions”被稱作predicates,但我這里不想引入新的編程術語。

  3. 這里有更通用的apply實現。

posted on 2012-04-12 16:41 coreBugZJ 閱讀(445) 評論(0)  編輯 收藏 引用 所屬分類: ProgrammingLanguage

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久精品在线| 亚洲国产一区二区三区a毛片| 亚洲桃色在线一区| 日韩小视频在线观看专区| 亚洲日韩欧美视频一区| 亚洲国产高清自拍| 亚洲激情在线| 9久re热视频在线精品| 亚洲最新合集| 欧美一区二区在线看| 久久视频这里只有精品| 久久久国际精品| 一本不卡影院| 亚洲影院色在线观看免费| 性色av一区二区三区| 久热精品在线视频| 欧美日韩精品免费观看视一区二区| 欧美午夜电影在线| 欧美高清在线精品一区| 日韩一区二区免费高清| 羞羞视频在线观看欧美| 欧美成年人视频网站欧美| 欧美性猛片xxxx免费看久爱 | 在线亚洲一区观看| 性亚洲最疯狂xxxx高清| 欧美成人乱码一区二区三区| 一本久久精品一区二区| 久久久综合精品| 欧美日韩在线电影| 亚洲第一黄网| 久久国产黑丝| 亚洲免费播放| 久久资源在线| 国产性色一区二区| 亚洲一区二区av电影| 蜜臀av性久久久久蜜臀aⅴ四虎| 日韩一级二级三级| 免费看的黄色欧美网站| 国产美女搞久久| 99精品国产热久久91蜜凸| 久久久亚洲欧洲日产国码αv| 99香蕉国产精品偷在线观看| 免费久久99精品国产自| 黑人一区二区| 久久精品国产免费| 一本大道av伊人久久综合| 麻豆久久婷婷| 在线精品高清中文字幕| 欧美一级片在线播放| 一本不卡影院| 国产精品第一页第二页第三页| 亚洲日本国产| 亚洲国产日韩一级| 免费欧美在线| 亚洲品质自拍| 亚洲国产精品久久久久秋霞影院| 久久亚洲二区| 亚洲国产日韩欧美在线图片| 六十路精品视频| 久久免费国产精品| 亚洲国产成人在线播放| 欧美成人免费大片| 欧美xx视频| 日韩视频在线播放| 亚洲欧洲精品天堂一级| 欧美激情国产日韩精品一区18| 亚洲国产人成综合网站| 久久五月天婷婷| 国产精品美女xx| 亚洲人永久免费| 亚洲国产一区在线观看| 欧美福利视频一区| 99在线精品观看| 日韩视频一区| 国产精品美女久久久久av超清| 亚洲一品av免费观看| 亚洲一区精品视频| 国内精品久久久久久久影视蜜臀| 久久性色av| 欧美大片在线观看| 亚洲一区二区三区在线看| 亚洲免费在线观看| 在线欧美小视频| 亚洲精品一区二区三区不| 国产精品久久久久久久久久ktv| 午夜亚洲性色福利视频| 久久成人久久爱| 99国产精品久久久久久久久久| 一区二区三区蜜桃网| 国产一区免费视频| 亚洲国产精品一区二区第一页| 欧美视频在线免费看| 久久综合给合久久狠狠色| 欧美日韩国产专区| 久久亚洲免费| 欧美性片在线观看| 欧美成人一区二免费视频软件| 欧美小视频在线| 欧美成人伊人久久综合网| 国产精品国产自产拍高清av王其| 欧美成人国产一区二区| 国产精品久久久久久久免费软件| 久久天天躁狠狠躁夜夜爽蜜月 | 久久婷婷亚洲| 中文欧美字幕免费| 久久久久久一区二区三区| 亚洲视频在线看| 欧美成人一区二区三区| 久久美女性网| 国产精品综合久久久| 亚洲人成人一区二区三区| 永久555www成人免费| 亚洲免费婷婷| 亚洲在线电影| 欧美日韩国产精品一区| 欧美二区视频| 国产原创一区二区| 亚洲视频观看| 在线视频免费在线观看一区二区| 久久久综合精品| 久久色在线播放| 一区二区激情小说| 亚洲美女黄色| 欧美成人一区二区三区在线观看 | 久久人人爽人人爽| 香蕉久久夜色精品国产| 欧美日韩国产一区二区三区| 国产精品露脸自拍| 国产一区二区在线观看免费| 久久国产手机看片| 欧美视频在线观看一区| 亚洲成色777777在线观看影院| 国外成人在线| 亚洲欧美日韩精品久久| 欧美亚洲一级| 国产精品乱码一区二区三区| 日韩一级大片在线| 亚洲手机成人高清视频| 欧美日韩在线高清| 99国产精品| 亚洲欧美日韩第一区| 国产精品成人在线观看| 亚洲性xxxx| 欧美一区在线看| 国内精品一区二区三区| 久久精品一区二区三区中文字幕 | 欧美一级欧美一级在线播放| 国产精品日韩欧美一区二区三区| 99精品视频免费| 亚洲在线成人| 国产乱理伦片在线观看夜一区 | 欧美激情第10页| 亚洲国产视频a| 欧美精品一区二区三区久久久竹菊| 91久久国产综合久久蜜月精品 | 日韩一二在线观看| 国产精品成人国产乱一区| 亚洲一区免费观看| 玖玖玖国产精品| 亚洲毛片播放| 国产精品视频yy9099| 久久国产欧美精品| 亚洲国产精品久久久久婷婷老年| 在线综合视频| 国产日韩综合| 欧美成人一区二区| 亚洲一区二区三区中文字幕| 久久综合给合久久狠狠色 | 老牛国产精品一区的观看方式| 亚洲国产毛片完整版| 欧美三级小说| 久久久精品一区| 99国产精品久久久久久久久久| 欧美日韩成人综合在线一区二区 | 亚洲欧美一区二区三区在线| 久久只精品国产| 亚洲视频你懂的| 激情欧美一区二区三区在线观看| 欧美精品自拍偷拍动漫精品| 欧美亚洲一区二区在线观看| 欧美激情一区三区| 欧美在线日韩在线| 日韩视频免费观看| 亚洲欧洲99久久| 亚洲精品在线视频| 国产精品久久久久久影院8一贰佰| 久久精品99国产精品酒店日本| 亚洲精品乱码久久久久久按摩观 | 欧美一区二区三区喷汁尤物| 亚洲电影下载| 国产精品一区二区视频| 欧美精品一区在线发布| 久久精品一区蜜桃臀影院 | 欧美激情精品久久久久久久变态| 亚洲永久免费精品| 日韩视频免费看| 亚洲国产美女| 欧美电影打屁股sp| 久久蜜桃香蕉精品一区二区三区| 亚洲午夜羞羞片|