関数結合子のみを使用したリンクリストの作成
19499 ワード
今日のようなデータ構造なしでリンクリストを作成する方法を示します
Object
or Arrays
; 代わりに、関数の組み合わせを使用します.私は、あなたがリンクスリストが何であるかについて、すでによく知られていると仮定しています.場合は、リンクリストには、チェックアウトを必要とする
.
私の目標は、あなたが前に見たことがないかもしれない何かを公開することです.Currying、部分的なアプリケーション、クロージャと関数の組み合わせで可能なものを表示します.そして、最も重要なのは、それをやっているときに少し楽しい時を過す.
⚠️ この記事にはrunkitが組み込まれています.このページの例では、実行、変更、微調整、および再生を意図します.
関数コンパレーターとは何か?
定義Thinking Functionally: Combinators
The word "combinator" is used to describe functions whose result depends only on their parameters. That means there is no dependency on the outside world, and in particular, no other functions or global value can be accessed at all.
In practice, this means that a combinator function is limited to combining its parameters in various ways.
それは取るために多くのので、多分いくつかの例が役立つでしょうか?
/* ☝️ These are combinators */
const I = a => a
const K = a => b => a
const V = a => b => c => c (a) (b)
const B = a => b => c => a (b (c))
// - - - ---------
// \ | / |
// arguments ---
// /
// only arguments are used
/* 👎 These are not */
const nope = a => a.map(double)
// --- ------
// / \
// / ⚠️ reaching outside of the func
// /
// ⚠️ can't use map either.
const add => a => b => a + b
// -
// /
// ⚠️ Uh oh, `+` is not part of 'arguments'
上記のコードを要約するには、コンテナーはその引数だけを使用できます.外部関数、メソッド、演算子を除外します.
心配しないで、それはまだ少し混乱して大丈夫です.(⊙_☉)
放棄構造
典型的なリンクリストは、これらのようなデータ構造を使用します.
class Node {
constructor(data, next) {
this.data = data
this.next = next
}
}
/* or */
const node = (data, next) => ({ data, next })
/* or */
const node = (data, next) => [ data, next ]
しかし、我々はこれらのデータ構造のどれも使用しないでしょう.関数の組み合わせを使用します.
コンビナータプールの右端に飛び込む前に、我々の基本機能から始めましょうnode
:
function node (data, next) {
// ---- ----
// / \
// our data the next node
}
ではどうやってアクセスするのdata
and next
使わずにnode
オブジェクトのような?あなたが言うならばcallbacks
, あなたは正しかった!
/////////////////
////
//📌 注意:これらのコードブロックを変更して実行できます!/
////
/////////////////
関数ノード( data , next , callback )
callback ( data , next )を返す
}
//バインドを使用してデータと次の値を保存できます.
ノード.bind ( null , data data , null )
//先頭から値を読み取るためにコールバックを使用します.
ヘッド( data , next )=>
データを返します
))>
私はこの実装を本当に気にしないbind
. だから私はカレーに行くnode
機能は、私は適用する部分的なアプリケーションを使用することができますdata
and next
. これは、bind
しかし、より良い構文で.
const node = data => next => callback => callback ( data ) ( next )
//-- -- --
//////
//パラメータはcurses ()--
////
//クロージャがデータを作成します
//最後にコールされたときにコールバックする.
//バインドを使用してデータと次の値を保存できます.
const head = node (' data ') ( NULL )
//------------
////
//引数データとNULLを部分的に適用できます.
//先頭から値を読み取るためにコールバックを使用します.
ヘッド( data =)next next >
データを返します
))>
あなたが非常に近い注意を払っていたならば、あなたはそれに気がつきましたnode
はV
上記の組み合わせ!
だから今node
に設定できます.
const node = V
そして、以下のようなノードを作成できます:
const evenOdd = node ('Even') ('Odd')
const leftRight = node ('Left') ('Right')
const yesNo = node ('Yes') ('No')
もし我々が部分的なアプリケーションが何をしているかのブレークダウンを見ているなら、次のようになります.
// first copy the node function
const evenOdd = data => next => callback => callback (data) (next)
// apply 'Even' to data.
const evenOdd = next => callback => callback ('Even') (next)
// apply 'Odd' to next.
const evenOdd = callback => callback ('Even') ('Odd')
// We end up with this:
const evenOdd = callback => callback ('Even') ('Odd')
evenOdd
では、callback
. The callback
以下のような関数を期待します:
const callback = a => b => { /* ??? */ }
我々は今、我々は再生を開始できるポイントです.ヒットplay
このrunkitで修正し、callback
返す'Left'
.
const v = a => b => c => c ( a )( b )
ノード= v
左端( node )
//todo :コールバックを変更し、コードが' left 'を返す
コールバック= A => B =>{ }
レフトライト( callback )///
再びコードを変更して'Right'
.
すごい!さあ、電話しましょう'Left'
機能data
と'Right'
機能next
.
const data = a => _ => a
const next = _ => b => b
我々の新しい機能でそれをもう一度走らせてください.
const v = a => b => c => c ( a )( b )
ノード= v
データを取得する
次のようになります
左端( node )
コンソール.ログ(左データ( data ))
コンソール.ログ(左)
あなたは気がつきましたかdata
も私たちと同じですK Combinator
?
// 💥 BOOM!
const data = K
next
ほとんどはK Combinator
, しかし、それは少し異なります.next
リターンb
, 中data
リターンa
. ちょっとしたトリックがあります.
// 🧙♀️ MAGIC!
const next = K (I)
このきちんとしたトリックは、全体の記事のためのインスピレーションでした.私はあなたが今2秒未満でこの問題を解決することができます賭ける!
そのリスト
我々がリンクされたリストに学んだものを翻訳しましょう.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')//末尾を検出するオブジェクトだけです.
const first = node ('1st') (Nil)
//
////
//nil終了
const second = node (' 2 ') ( 1 )
//---
////
//次のノードとして最初のノードを渡します
const番目のノード( 3番目)
//---
////
//2番目のノードを次のノードとして渡す
コンソール.ログ( 3番目(データ))//=> ' 3 '
コンソール.log ( 3番目の(データ))//=> ' 2 nd '
コンソール.log ( 3番目( next ) ( data ))///=> ' 1 '
カウントリスト
リストを列挙してカウントを返す簡単な関数を作成できます.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
const length =( list , value = 0 )=>
リストの一覧
?値
: length ( list ( next ), value + 1 )
const first = node ('1st') (Nil)
const second = node (' 2 ') ( 1 )
const番目のノード( 3番目)
コンソール.log ( length ( 1 ))///= 1
コンソール.log ( length ( 2 ))///= 2
コンソール.ログ( length ( 3 ))///= 3
マップリスト
マッピングはArray
.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
のリストを表示します
リストの一覧
?リスト
: node ( func ( list ( data )))( map ( func ( list ( next )) ))
const first = node ('1st') (Nil)
const second = node (' 2 ') ( 1 )
const番目のノード( 3番目)
const upper = x = > x . touppercase ()
const second = map ( 3番目)
コンソール.ログ( 3番目(データ))//=> ' 3 '
コンソール.log ( 3番目の(データ))//=> ' 2 nd '
コンソール.log ( 3番目( next ) ( data ))///=> ' 1 '
フィルタ
フィルタリングもArray
.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
const filter = predicate => list =>
リスト== Nil?リスト
:述語( list )node ( list ( data ))( filter ( predicate ) ( list ( next ))
: filter ( predicate ) ( list ( next )
const first = node (1) (Nil)
第2ノード( 2 )(第1回)
const番目のノード( 3 )( 2 )
const 4番目のノード( 4 )( 3番目)
x = 0 = 0 =
コンセントevens =フィルタ( iSeven ) ( 4 )
コンソール.ログ( evens ( data ))///> 4
コンソール.log ( evens ( next ))
しかし、関数の組み合わせは本当に役に立ちますか?
確かに、この方法でリンクリストを作成する必要はありません.実際には、まずリンクリストを作成する必要はありません.それで、これはとにかくとにかくアカデミックです.
驚くべきことに、関数結合器の実用的な用途がいくつかあります!
あなたは、認識しないかもしれませんB Combinator
const B = a => b => c => a (b (c))
書かれていない限り
const compose = f => g => x => f (g (x))
そうですね.compose
はまさにB Combinator
! あなたが好奇心旺盛ならpipe
はQ Combinator
.
もう一つの有用なユーティリティ機能ですalways
. ラムダはalways
図書館で.また、単純な機能の組み合わせでそれを再現することができます.
const always = K
const awesome = always ('Awesome!')
awesome () //=> 'Awesome!'
awesome (123) //=> 'Awesome!'
awesome ('hello') //=> 'Awesome!'
tap
私はよく使用する共通の関数です.以下のように書くことができます.それは副作用を管理するための素晴らしいです.
const tap = func => val => {
func (val) // execute my side effect
return val // return the original value
}
私たちも書くことができましたtap
このように:
const tap = S (K)
これは、関数の組み合わせで作成することができます本当に便利なものの多くです!
概要
The word "combinator" is used to describe functions whose result depends only on their parameters. That means there is no dependency on the outside world, and in particular, no other functions or global value can be accessed at all.
In practice, this means that a combinator function is limited to combining its parameters in various ways.
/* ☝️ These are combinators */
const I = a => a
const K = a => b => a
const V = a => b => c => c (a) (b)
const B = a => b => c => a (b (c))
// - - - ---------
// \ | / |
// arguments ---
// /
// only arguments are used
/* 👎 These are not */
const nope = a => a.map(double)
// --- ------
// / \
// / ⚠️ reaching outside of the func
// /
// ⚠️ can't use map either.
const add => a => b => a + b
// -
// /
// ⚠️ Uh oh, `+` is not part of 'arguments'
典型的なリンクリストは、これらのようなデータ構造を使用します.
class Node {
constructor(data, next) {
this.data = data
this.next = next
}
}
/* or */
const node = (data, next) => ({ data, next })
/* or */
const node = (data, next) => [ data, next ]
しかし、我々はこれらのデータ構造のどれも使用しないでしょう.関数の組み合わせを使用します.コンビナータプールの右端に飛び込む前に、我々の基本機能から始めましょう
node
:function node (data, next) {
// ---- ----
// / \
// our data the next node
}
ではどうやってアクセスするのdata
and next
使わずにnode
オブジェクトのような?あなたが言うならばcallbacks
, あなたは正しかった!/////////////////
////
//📌 注意:これらのコードブロックを変更して実行できます!/
////
/////////////////
関数ノード( data , next , callback )
callback ( data , next )を返す
}
//バインドを使用してデータと次の値を保存できます.
ノード.bind ( null , data data , null )
//先頭から値を読み取るためにコールバックを使用します.
ヘッド( data , next )=>
データを返します
))>
私はこの実装を本当に気にしない
bind
. だから私はカレーに行くnode
機能は、私は適用する部分的なアプリケーションを使用することができますdata
and next
. これは、bind
しかし、より良い構文で.const node = data => next => callback => callback ( data ) ( next )
//-- -- --
//////
//パラメータはcurses ()--
////
//クロージャがデータを作成します
//最後にコールされたときにコールバックする.
//バインドを使用してデータと次の値を保存できます.
const head = node (' data ') ( NULL )
//------------
////
//引数データとNULLを部分的に適用できます.
//先頭から値を読み取るためにコールバックを使用します.
ヘッド( data =)next next >
データを返します
))>
あなたが非常に近い注意を払っていたならば、あなたはそれに気がつきました
node
はV
上記の組み合わせ!だから今
node
に設定できます.const node = V
そして、以下のようなノードを作成できます:const evenOdd = node ('Even') ('Odd')
const leftRight = node ('Left') ('Right')
const yesNo = node ('Yes') ('No')
もし我々が部分的なアプリケーションが何をしているかのブレークダウンを見ているなら、次のようになります.// first copy the node function
const evenOdd = data => next => callback => callback (data) (next)
// apply 'Even' to data.
const evenOdd = next => callback => callback ('Even') (next)
// apply 'Odd' to next.
const evenOdd = callback => callback ('Even') ('Odd')
// We end up with this:
const evenOdd = callback => callback ('Even') ('Odd')
evenOdd
では、callback
. The callback
以下のような関数を期待します:const callback = a => b => { /* ??? */ }
我々は今、我々は再生を開始できるポイントです.ヒットplay
このrunkitで修正し、callback
返す'Left'
.const v = a => b => c => c ( a )( b )
ノード= v
左端( node )
//todo :コールバックを変更し、コードが' left 'を返す
コールバック= A => B =>{ }
レフトライト( callback )///
再びコードを変更して
'Right'
.すごい!さあ、電話しましょう
'Left'
機能data
と'Right'
機能next
.const data = a => _ => a
const next = _ => b => b
我々の新しい機能でそれをもう一度走らせてください.const v = a => b => c => c ( a )( b )
ノード= v
データを取得する
次のようになります
左端( node )
コンソール.ログ(左データ( data ))
コンソール.ログ(左)
あなたは気がつきましたか
data
も私たちと同じですK Combinator
?// 💥 BOOM!
const data = K
next
ほとんどはK Combinator
, しかし、それは少し異なります.next
リターンb
, 中data
リターンa
. ちょっとしたトリックがあります.// 🧙♀️ MAGIC!
const next = K (I)
このきちんとしたトリックは、全体の記事のためのインスピレーションでした.私はあなたが今2秒未満でこの問題を解決することができます賭ける!そのリスト
我々がリンクされたリストに学んだものを翻訳しましょう.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')//末尾を検出するオブジェクトだけです.
const first = node ('1st') (Nil)
//
////
//nil終了
const second = node (' 2 ') ( 1 )
//---
////
//次のノードとして最初のノードを渡します
const番目のノード( 3番目)
//---
////
//2番目のノードを次のノードとして渡す
コンソール.ログ( 3番目(データ))//=> ' 3 '
コンソール.log ( 3番目の(データ))//=> ' 2 nd '
コンソール.log ( 3番目( next ) ( data ))///=> ' 1 '
カウントリスト
リストを列挙してカウントを返す簡単な関数を作成できます.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
const length =( list , value = 0 )=>
リストの一覧
?値
: length ( list ( next ), value + 1 )
const first = node ('1st') (Nil)
const second = node (' 2 ') ( 1 )
const番目のノード( 3番目)
コンソール.log ( length ( 1 ))///= 1
コンソール.log ( length ( 2 ))///= 2
コンソール.ログ( length ( 3 ))///= 3
マップリスト
マッピングはArray
.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
のリストを表示します
リストの一覧
?リスト
: node ( func ( list ( data )))( map ( func ( list ( next )) ))
const first = node ('1st') (Nil)
const second = node (' 2 ') ( 1 )
const番目のノード( 3番目)
const upper = x = > x . touppercase ()
const second = map ( 3番目)
コンソール.ログ( 3番目(データ))//=> ' 3 '
コンソール.log ( 3番目の(データ))//=> ' 2 nd '
コンソール.log ( 3番目( next ) ( data ))///=> ' 1 '
フィルタ
フィルタリングもArray
.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
const filter = predicate => list =>
リスト== Nil?リスト
:述語( list )node ( list ( data ))( filter ( predicate ) ( list ( next ))
: filter ( predicate ) ( list ( next )
const first = node (1) (Nil)
第2ノード( 2 )(第1回)
const番目のノード( 3 )( 2 )
const 4番目のノード( 4 )( 3番目)
x = 0 = 0 =
コンセントevens =フィルタ( iSeven ) ( 4 )
コンソール.ログ( evens ( data ))///> 4
コンソール.log ( evens ( next ))
しかし、関数の組み合わせは本当に役に立ちますか?
確かに、この方法でリンクリストを作成する必要はありません.実際には、まずリンクリストを作成する必要はありません.それで、これはとにかくとにかくアカデミックです.
驚くべきことに、関数結合器の実用的な用途がいくつかあります!
あなたは、認識しないかもしれませんB Combinator
const B = a => b => c => a (b (c))
書かれていない限り
const compose = f => g => x => f (g (x))
そうですね.compose
はまさにB Combinator
! あなたが好奇心旺盛ならpipe
はQ Combinator
.
もう一つの有用なユーティリティ機能ですalways
. ラムダはalways
図書館で.また、単純な機能の組み合わせでそれを再現することができます.
const always = K
const awesome = always ('Awesome!')
awesome () //=> 'Awesome!'
awesome (123) //=> 'Awesome!'
awesome ('hello') //=> 'Awesome!'
tap
私はよく使用する共通の関数です.以下のように書くことができます.それは副作用を管理するための素晴らしいです.
const tap = func => val => {
func (val) // execute my side effect
return val // return the original value
}
私たちも書くことができましたtap
このように:
const tap = S (K)
これは、関数の組み合わせで作成することができます本当に便利なものの多くです!
概要
リストを列挙してカウントを返す簡単な関数を作成できます.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
const length =( list , value = 0 )=>
リストの一覧
?値
: length ( list ( next ), value + 1 )
const first = node ('1st') (Nil)
const second = node (' 2 ') ( 1 )
const番目のノード( 3番目)
コンソール.log ( length ( 1 ))///= 1
コンソール.log ( length ( 2 ))///= 2
コンソール.ログ( length ( 3 ))///= 3
マップリスト
マッピングはArray
.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
のリストを表示します
リストの一覧
?リスト
: node ( func ( list ( data )))( map ( func ( list ( next )) ))
const first = node ('1st') (Nil)
const second = node (' 2 ') ( 1 )
const番目のノード( 3番目)
const upper = x = > x . touppercase ()
const second = map ( 3番目)
コンソール.ログ( 3番目(データ))//=> ' 3 '
コンソール.log ( 3番目の(データ))//=> ' 2 nd '
コンソール.log ( 3番目( next ) ( data ))///=> ' 1 '
フィルタ
フィルタリングもArray
.
A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
const filter = predicate => list =>
リスト== Nil?リスト
:述語( list )node ( list ( data ))( filter ( predicate ) ( list ( next ))
: filter ( predicate ) ( list ( next )
const first = node (1) (Nil)
第2ノード( 2 )(第1回)
const番目のノード( 3 )( 2 )
const 4番目のノード( 4 )( 3番目)
x = 0 = 0 =
コンセントevens =フィルタ( iSeven ) ( 4 )
コンソール.ログ( evens ( data ))///> 4
コンソール.log ( evens ( next ))
しかし、関数の組み合わせは本当に役に立ちますか?
確かに、この方法でリンクリストを作成する必要はありません.実際には、まずリンクリストを作成する必要はありません.それで、これはとにかくとにかくアカデミックです.
驚くべきことに、関数結合器の実用的な用途がいくつかあります!
あなたは、認識しないかもしれませんB Combinator
const B = a => b => c => a (b (c))
書かれていない限り
const compose = f => g => x => f (g (x))
そうですね.compose
はまさにB Combinator
! あなたが好奇心旺盛ならpipe
はQ Combinator
.
もう一つの有用なユーティリティ機能ですalways
. ラムダはalways
図書館で.また、単純な機能の組み合わせでそれを再現することができます.
const always = K
const awesome = always ('Awesome!')
awesome () //=> 'Awesome!'
awesome (123) //=> 'Awesome!'
awesome ('hello') //=> 'Awesome!'
tap
私はよく使用する共通の関数です.以下のように書くことができます.それは副作用を管理するための素晴らしいです.
const tap = func => val => {
func (val) // execute my side effect
return val // return the original value
}
私たちも書くことができましたtap
このように:
const tap = S (K)
これは、関数の組み合わせで作成することができます本当に便利なものの多くです!
概要
フィルタリングも
Array
.A = A = A
コンストK = A => B => A
const v = a => b => c => c ( a )( b )
ノード= v
データ標準
次のようにします.
const nil = symbol (' nil ')
//この実装を心配しないでください.
//以下のコードをデモするだけです.
const filter = predicate => list =>
リスト== Nil?リスト
:述語( list )node ( list ( data ))( filter ( predicate ) ( list ( next ))
: filter ( predicate ) ( list ( next )
const first = node (1) (Nil)
第2ノード( 2 )(第1回)
const番目のノード( 3 )( 2 )
const 4番目のノード( 4 )( 3番目)
x = 0 = 0 =
コンセントevens =フィルタ( iSeven ) ( 4 )
コンソール.ログ( evens ( data ))///> 4
コンソール.log ( evens ( next ))
しかし、関数の組み合わせは本当に役に立ちますか?
確かに、この方法でリンクリストを作成する必要はありません.実際には、まずリンクリストを作成する必要はありません.それで、これはとにかくとにかくアカデミックです.
驚くべきことに、関数結合器の実用的な用途がいくつかあります!
あなたは、認識しないかもしれませんB Combinator
const B = a => b => c => a (b (c))
書かれていない限り
const compose = f => g => x => f (g (x))
そうですね.compose
はまさにB Combinator
! あなたが好奇心旺盛ならpipe
はQ Combinator
.
もう一つの有用なユーティリティ機能ですalways
. ラムダはalways
図書館で.また、単純な機能の組み合わせでそれを再現することができます.
const always = K
const awesome = always ('Awesome!')
awesome () //=> 'Awesome!'
awesome (123) //=> 'Awesome!'
awesome ('hello') //=> 'Awesome!'
tap
私はよく使用する共通の関数です.以下のように書くことができます.それは副作用を管理するための素晴らしいです.
const tap = func => val => {
func (val) // execute my side effect
return val // return the original value
}
私たちも書くことができましたtap
このように:
const tap = S (K)
これは、関数の組み合わせで作成することができます本当に便利なものの多くです!
概要
const B = a => b => c => a (b (c))
const compose = f => g => x => f (g (x))
const always = K
const awesome = always ('Awesome!')
awesome () //=> 'Awesome!'
awesome (123) //=> 'Awesome!'
awesome ('hello') //=> 'Awesome!'
const tap = func => val => {
func (val) // execute my side effect
return val // return the original value
}
const tap = S (K)
あなたがRunKitの例について考えたことを知らせてください.私は私のポストにそれらを組み込むことを検討しています.
関数の組み合わせについてもっと知りたいですか?コメントで知らせてください!
あなたが機能JavaScriptが大好きな場合は、ここまたはTwitterで私に従ってください!
Reference
この問題について(関数結合子のみを使用したリンクリストの作成), 我々は、より多くの情報をここで見つけました https://dev.to/joelnet/creating-a-linked-list-using-only-function-combinators-5hhkテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol