オブジェクト指向プログラミングを学習し、使用するのに長い時間の後、背中のステップやシステムの複雑さを考えてみましょう。
いくつかの研究を行った後、私はそのような不変性と純粋な関数として、関数型プログラミングの概念を発見しました。これらの概念は、副作用なしに機能を構築することができますので、保守が容易なシステムは、他の利点があります。
この記事では、我々は詳細に関連する関数型プログラミングや重要な概念のいくつかを説明するためのコード例の多くを通過します。
関数型プログラミングとは何ですか
関数型プログラミングは、数学関数の評価を計算するために考えられている、プログラミングパラダイム、構造及びスタイル要素を構築するためのコンピュータプログラムであると状態変数データの変化を避けることができます。
ピュア機能
私たちは関数型プログラミングを理解したいときは、まず、基本的な概念を知っている必要があり、純粋な関数であるが、純粋な機能は何地獄ですか?
私たちは純粋な機能はここに非常に厳密な定義があるかどうかの関数である方法を知っています?:
- 同じパラメータが与えられると、同じ結果が返される(とも呼ばれる決定論的)。
- これは、任意の副作用を引き起こすことはありません。
同じパラメータを指定すると、同じ結果が得られ、
同じパラメータを指定した場合は、同じ結果を返します。私たちは、円の面積を計算する機能を実装することを想像してみてください。
関数は、半径計算し、パラメータとして半径を受けて、純粋ない半径を PI:
let PI = 3.14;
const calculateArea = (radius) => radius * radius * PI;
calculateArea(10); // returns 314.0
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路
なぜこれが不純な関数である?それはグローバルオブジェクトがパラメータとして関数に渡されていない使用しているので理由は、簡単です。
さて、想像数学者の数は、PIの値は、実際に42で、グローバルオブジェクトの値を変更すると信じています。
不純な機能は、10入手10 42 = 4200。同じパラメータ(半径= 10)のために、我々は、異なる結果を得ます。
それを修正します:
let PI = 3.14;
const calculateArea = (radius, pi) => radius * radius * pi;
calculateArea(10, PI); // returns 314.0
外部オブジェクトが導入されないように、今、PIの値は、引数として関数に渡されます。
- 314.0:半径= 10とPI = 3.14のパラメータは、常に同じ結果です。
- 半径= 10とPI = 42のため、常に同じ結果を得る:4200
ファイルを読みます
次の関数は、それが純粋な関数ではありません、外部ファイルを読み込んで、いつでもファイルの内容が異なる場合があります。
const charactersCounter = (text) => `Character count: ${text.length}`;
function analyzeFile(filename) {
let fileContent = open(filename);
return charactersCounter(fileContent);
}
乱数ジェネレータ
任意の関数は純粋であることができない乱数ジェネレータの機能に依存します。
function yearEndEvaluation() {
if (Math.random() > 0.5) {
return "You get a raise!";
} else {
return "Better luck next year!";
}
}
有意な副作用がありません
純粋関数は、任意の副作用が観察されることはありません。副作用の例としては、可視オブジェクトを含むまたは参照によって渡さグローバルパラメータを変更します。
今、私たちは、整数を受け取り、インクリメントされた整数とリターンを返す機能を実現します。
let counter = 1;
function increaseCounter(value) {
counter = value + 1;
}
increaseCounter(counter);
console.log(counter); // 2
不純な関数は値を受け取り、その値1を増加させるために、カウンタを再割り当てします。
関数型プログラミングは、変動を阻止します。私たちは、グローバルオブジェクトを修正するが、どのようにそれの純粋な機能にするために行うには?単に1の付加価値を返します。
let counter = 1;
const increaseCounter = (value) => value + 1;
increaseCounter(counter); // 2
console.log(counter); // 1
IncreaseCounter純粋な関数は2を返しますが、カウンタの値は同じです。この関数は、変数の値を変更せずに、増分された値を返します。
我々はこれらの2つの単純なルールに従うならば、私たちのプログラムを理解することが容易になります。今、それぞれの機能が分離され、それがシステムの他の部分に影響を与えることができません。
純粋関数は、安定した一貫性と予測可能です。同じパラメータを考えると、純粋な関数が常に同じ結果を返します。
それが起こることはありませんので、我々は、さまざまな状況を持っている同じパラメータの結果を検討する必要があります。
純粋な機能のメリット
純粋な機能は確かにコードをテストする方が簡単です、何かをモックする必要はありませんので、我々は別のコンテキストをテストし、純粋な機能ユニットを使用することができます。
- パラメータA与えられ、関数は所望の値Bを返します
- パラメータCが与えられると、この関数はD、所望の値を返します
簡単な例は、デジタル受信のセット、1だけインクリメントされ、各そのような砂の彫刻の動作数です。
let list = [1, 2, 3, 4, 5];
const incrementNumbers = (list) => list.map(number => number + 1);
数字の配列を受けて、各番号は、マップを使用してインクリメントされ、数字の新しい増分リストを返します。
incrementNumbers(list); // [2, 3, 4, 5, 6]
入力[1,2,3,4,5]のために、期待される出力は、[2,3,4,5,6]です。
プログラム上の誰もが、ウェブのフロントエンドに興味を持って、業界の友人の深い理解に、計画を学ぶために勉強したい場合は、私たちは、フロントエンドの学習バックルqunを追加することができます:784783012人の友人を、あなたは私が歓迎している、学生であるか、切り替えたいかどうか、ではありません誰もが共有するために定期的に乾燥し、Webフロントエンドを終えた2019年最新の学習教材と0の基本チュートリアルを共有:学習前、我々は真剣に考えています
不変性
時間変更または同じにもかかわらず、純粋な機能の大物は同じです。
データが不変である場合には、それが作成された後、その状態を変更することはできません。
あなたは、不変オブジェクトを変更するには、ハード来ている場合は、ちょうど深いコピーにコピーし、コピー操作を必要とすることはできません。
JSでは、我々は通常、私は、変数の変数である各反復のために、forループを使用します。
var values = [1, 2, 3, 4, 5];
var sumOfValues = 0;
for (var i = 0; i < values.length; i++) {
sumOfValues += values[i];
}
sumOfValues // 15
各反復のために、iと状態のsumOfValueの変更が、どのように我々はトラバーサルの変動に対処するのですか?答えは、使用することです再帰を。
let list = [1, 2, 3, 4, 5];
let accumulator = 0;
function sum(list, accumulator) {
if (list.length == 0) {
return accumulator;
}
return sum(list.slice(1), accumulator + list[0]);
}
sum(list, accumulator); // 15
list; // [1, 2, 3, 4, 5]
accumulator; // 0
上記のコードは、それがベクトル値を受信し、和関数です。出口リストが空になるまでこの関数は自分自身を再帰的に呼び出します。各「歩く」ために、我々は、アキュムレータの合計値に追加されます。
再帰の使用は、変数が一定のままです。これは、リストとアキュムレータ変数を変更しません。これは、同じ値を保持します。
観察:私たちは、この機能を実現するために使用を減らすことができます。この議論はで高階関数にコンテンツを取りました。
オブジェクトの最終状態の構築も非常に一般的です。私たちは、URLスラグに文字列を変換しようと、文字列があるとします。
オブジェクト指向プログラミングではルビー、私たちはクラスUrlSlugifyを作成することができ、このクラスは、文字列slugify入力されたURLスラグを変換する方法があります。
class UrlSlugify
attr_reader :text
def initialize(text)
@text = text
end
def slugify!
text.downcase!
text.strip!
text.gsub!(' ', '-')
end
end
UrlSlugify.new(' I will be a url slug ').slugify! # "i-will-be-a-url-slug"
そこ私たちは、各slugify過程で何をしたいのか、すべて小文字の最初の上記の使用命令型プログラミング方法、およびその後、未使用のスペースを削除し、ハイフンで最後に残ったスペースを交換してください。
それは全体のプロセスでの入力状態を変更する。このようにして、純粋な関数の概念に明らかに矛盾しています。
ここでは関数または関数チェーンの組み合わせにより最適化することができます。換言すれば、次の関数の入力として、元の入力文字列を変更することなく、関数の結果。
const string = " I will be a url slug ";
const slugify = string =>
string
.toLowerCase()
.trim()
.split(" ")
.join("-");
slugify(string); // i-will-be-a-url-slug
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路
コードはこれらの事を行うには主に次のとおりです。
- toLowerCaseメソッド:すべて小文字に文字列を変換します。
- トリム:文字列の空白の両端を削除します。
- 分割と結合:指定した文字列の置換に一致するすべてのインスタンスを置き換えます
参照透明性
そして、正方形の機能を実装します。
const square = (n) => n * n;
同じ入力に設定し、この関数は常に同じ正味出力を持つことになります。
square(2); // 4
square(2); // 4
square(2); // 4
// ...
パラメータ平方関数は常に4を返すよう2が渡されます。このように、我々は、(2)4の中に、私たちの関数は参照透明である正方形ことができます。
関数が常に同じ入力に対して同じ結果を生成する場合、基本的に、それは透明とみなすことができます。
この概念では、我々は覚えておくべきクールな事は、この機能で行うことができます。機能を想像してみて
const sum = (a, b) => a + b;
これらのパラメータは、それを呼び出すために使用されています
sum(3, sum(5, 8));
合計(5、8)は、常にあなたがショーの操作を行うことができ、13に等しいです:
sum(3, 13);
この式16は、常に我々は一定の値を持つ式全体を交換し、それを書き留めすることができ、得られます。
関数は、JSの市民であります
市民の関数としてのJSがトレンドで、機能も考慮され、データを使用するための値として使用することができます。
- これは、定数と変数から引用しました。
- これは他の関数にパラメータとして渡されました。
- 他の関数の結果を返します。
アイデアは、データ転送としての価値と機能として機能することです。このように、我々は新しい行動で新しい関数を作成するために、さまざまな機能を組み合わせることができます。
我々は、2つの値が加算される機能を持っている場合、以下に示すように、値は、倍になります。
const doubleSum = (a, b) => (a + b) * 2;
対応する2つの値を差分し、値が2倍になります。
const doubleSubtraction = (a, b) => (a - b) * 2;
これらの機能はなく、その演算子関数の違いと、同様のロジックを持っています。我々は関数値とみなしてパラメータとして渡すことができれば、我々は関数内でそれを使用する受信者動作機能と機能を構築することができます。
const sum = (a, b) => a + b;
const subtraction = (a, b) => a - b;
const doubleOperator = (f, a, b) => f(a, b) * 2;
doubleOperator(sum, 3, 1); // 8
doubleOperator(subtraction, 3, 1); // 4
fおよび総和伝達関数とdoubleOperator挙動を用いて減算機能とは、新しい組み合わせを作成パラメータa及びbを処理するために使用します。
高次機能
我々は高階関数について話すとき、一般的に次のものがあります。
- の関数として、一つ以上のパラメータ
- の関数として結果を返します。
doubleOperator関数は、関数ためのパラメータとして、上記の高次機能を達成するために、オペレータであり、それを使用します。
私たちはしばしば、フィルタ、マップを使用して減らす高階関数です、見る見。
フィルタ
特定のセットのために、我々は属性によってフィルタリングします。フィルタ関数は、要素が結果セットに含まれるべきかどうかを決定するために真または偽の値を期待します。
コールバック式が真の場合、フィルタ機能は、そうでない場合、結果セットの要素が含まれ、それはしません。
簡単な例では、我々は整数の集合を持っているとき、我々は唯一でもしたいです。
命令
でも通常行うすべての配列を取得するために不可欠な方法を使用します。
- 空の配列evenNumbersを作成します。
- 配列番号を通じて、
- でも、配列evenNumbersをプッシュします
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var evenNumbers = [];
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 == 0) {
evenNumbers.push(numbers[i]);
}
}
console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10]
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路
我々はまたしても高階関数を受信するフィルタ機能を使用して、偶数のリストを返すことができます。
CONSTさえ= N => N%2 == 0。
CONST listOfNumbersの= [0、1、2、3、4、5、6、7、8、9、10]。
listOfNumbers.filter(さえ)。// [0、2、4、6、8、10]
命令型のアプローチは、このように、通常は次のとおりです。
var filterArray = function(x, coll) {
var resultArray = [];
for (var i = 0; i < coll.length; i++) {
if (coll[i] < x) {
resultArray.push(coll[i]);
}
}
return resultArray;
}
console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0]
宣言的な方法
必ず上記のために、我々は次のようにこの問題を解決するために、より宣言的なアプローチをしたいです:
function smaller(number) {
return number < this;
}
function filterArray(x, listOfNumbers) {
return listOfNumbers.filter(smaller, x);
}
let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0];
filterArray(3, numbers); // [2, 1, 0]
少し奇妙に見えるし始め、この機能に小さく使用していますが、理解しやすいです。
この上述した第2のパラメータのフィルタ関数、xの値すなわち。
我々はまた、それを行うには、マップの方法を使用することができます。情報の集合を想像してみて
let people = [
{ name: "TK", age: 26 },
{ name: "Kaio", age: 10 },
{ name: "Kazumi", age: 30 }
]
私たちは、フィルタモードで、21歳以上の年齢の方が大きいフィルタリングします
const olderThan21 = person => person.age > 21;
const overAge = people => people.filter(olderThan21);
overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }]
地図
主なアイデアは、マップ機能のコレクションを変換することです。
そのすべての要素をマッピングする関数を適用し、変換により返された値に基づいて、集合の新しいセットを構築することにより方法。
私たちは21歳以上の人をフィルタリングしたくない場合は、私たちは何をしたい、このように表示されている:TKは26歳です。
不可欠使って、我々は通常の操作を行います。
var people = [
{ name: "TK", age: 26 },
{ name: "Kaio", age: 10 },
{ name: "Kazumi", age: 30 }
];
var peopleSentences = [];
for (var i = 0; i < people.length; i++) {
var sentence = people[i].name + " is " + people[i].age + " years old";
peopleSentences.push(sentence);
}
console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']
宣言型の操作を行います。
const makeSentence = (person) => `${person.name} is ${person.age} years old`;
const peopleSentences = (people) => people.map(makeSentence);
peopleSentences(people);
// ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']
全体的なアイデアは、新しい配列に指定された配列を変換することです。
もう一つの興味深い質問はHackerRankがある問題のリストを更新します。私たちは、その値を更新するために、アレイの絶対値を使用します。
例えば、入力[2,3、 - 4,5]要求出力[1,2,3,4,5]、 - 4の絶対値は4です。
簡単な解決策は、値の各更新されたセットを配置することで、非常に危険な練習
var values = [1, 2, 3, -4, 5];
for (var i = 0; i < values.length; i++) {
values[i] = Math.abs(values[i]);
}
console.log(values); // [1, 2, 3, 4, 5]
私たちは、Math.abs関数はその絶対値の値とインプレース更新を変換します。
これは、ソリューションを行うための最善の方法ではありません。
まず第一に、私たちは不変性を知って、フロント不変性を学んだ、より一貫性と予測可能な機能を作り、私たちのアイデアは、新しいコレクションは、すべて絶対に持って作成することでした。
第二に、なぜここにすべてのデータを「変換」するためにマップを使用しません
私の最初のアイデアは、Math.abs機能が一つの値だけを扱う試験することでした。
Math.abs(-1); // 1
Math.abs(1); // 1
Math.abs(-2); // 2
Math.abs(2); // 2
私たちは、正の値(絶対値)に各値を変換したいです。
今すぐ値で絶対値操作を実行する方法を知って、この関数は、関数のマップに渡されたパラメータとして使用することができます。
高階関数は、関数のパラメータとして、それを受信して使用することができます覚えていますか?はい、マップ機能は、それを行うことができます
let values = [1, 2, 3, -4, 5];
const updateListMap = (values) => values.map(Math.abs);
updateListMap(values); // [1, 2, 3, 4, 5]
減らします
受信機能および値のセットで、作成したこれらの項目の組み合わせを返す関数を減らすと思いました。
一般的な例は、取得した注文の合計量です。
あなたがショッピングサイトであると仮定し、私たちは1、2製品を持って、製品および製品3 4カート製品(オーダー)で追加します。今、私たちは、ショッピングカートの合計数を計算する必要があります。
命令的な方法に注文し、各製品の量との和の合計額の便利なリストです。
var orders = [
{ productTitle: "Product 1", amount: 10 },
{ productTitle: "Product 2", amount: 30 },
{ productTitle: "Product 3", amount: 20 },
{ productTitle: "Product 4", amount: 60 }
];
var totalAmount = 0;
for (var i = 0; i < orders.length; i++) {
totalAmount += orders[i].amount;
}
console.log(totalAmount); // 120
我々はパラメータとして伝達関数を減らすために合計し、処理量を算出する機能を構築することができ、削減使用してください。
let shoppingCart = [
{ productTitle: "Product 1", amount: 10 },
{ productTitle: "Product 2", amount: 30 },
{ productTitle: "Product 3", amount: 20 },
{ productTitle: "Product 4", amount: 60 }
];
const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount;
const getTotalAmount = (shoppingCart) => shoppingCart.reduce(sumAmount, 0);
getTotalAmount(shoppingCart); // 120
そこショッピングカート、それらを合計し、現在currentTotalAmount sumAmountを受信する機能、およびオブジェクトの順序。
あなたはショッピングカートが設定量に換算することもマップを使用して、関数sumAmount機能を減らすことができます。
CONST getAmount =(順)=> order.amount。
CONST sumAmount =(ACC、量)=> ACC +量。
function getTotalAmount(shoppingCart) {
return shoppingCart
.map(getAmount)
.reduce(sumAmount, 0);
}
getTotalAmount(shoppingCart); // 120
getAmount製品受信オブジェクトを返すだけの量の値、すなわち、[10,30,20,60]、その後、一緒にすべてのアイテムを減らす添加することによって。
3つの機能の例
各高階関数の作品を読みます。ここでは簡単な例では、これらの3つの機能を結合する方法を説明し、お見せする例があります。
それは、ショッピングカートに来るとき、私たちはオーダーリストに、この製品を持っていると仮定
let shoppingCart = [
{ productTitle: "Functional Programming", type: "books", amount: 10 },
{ productTitle: "Kindle", type: "eletronics", amount: 30 },
{ productTitle: "Shoes", type: "fashion", amount: 20 },
{ productTitle: "Clean Code", type: "books", amount: 60 }
]
お車で買い物をしたい場合は、通常は行う冊の合計数を入力します。
- 書籍のフィルタの種類
- ショッピングカートは、収集量で変換するにはマップを使用してください。
- すべては、使用量の削減にまで追加します。
let shoppingCart = [
{ productTitle: "Functional Programming", type: "books", amount: 10 },
{ productTitle: "Kindle", type: "eletronics", amount: 30 },
{ productTitle: "Shoes", type: "fashion", amount: 20 },
{ productTitle: "Clean Code", type: "books", amount: 60 }
]
const byBooks = (order) => order.type == "books";
const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;
function getTotalAmount(shoppingCart) {
return shoppingCart
.filter(byBooks)
.map(getAmount)
.reduce(sumAmount, 0);
}
getTotalAmount(shoppingCart); // 70
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路