ฉันเคยเห็นการอ้างอิงถึงฟังก์ชั่น curated ในหลายบทความและบล็อก แต่ฉันไม่สามารถหาคำอธิบายที่ดี (หรืออย่างน้อยหนึ่งที่ทำให้รู้สึก!)
add x y = x+y
(curried) นั้นแตกต่างจากadd (x, y)=x+y
(uncurried)
ฉันเคยเห็นการอ้างอิงถึงฟังก์ชั่น curated ในหลายบทความและบล็อก แต่ฉันไม่สามารถหาคำอธิบายที่ดี (หรืออย่างน้อยหนึ่งที่ทำให้รู้สึก!)
add x y = x+y
(curried) นั้นแตกต่างจากadd (x, y)=x+y
(uncurried)
คำตอบ:
การ Currying คือเมื่อคุณแยกฟังก์ชันที่ใช้อาร์กิวเมนต์หลาย ๆ ตัวลงในชุดฟังก์ชันที่แต่ละอาร์กิวเมนต์มีเพียงอาร์กิวเมนต์เดียวเท่านั้น นี่คือตัวอย่างใน JavaScript:
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
นี่คือฟังก์ชันที่รับอาร์กิวเมนต์สองตัวคือ a และ b และคืนค่าผลรวม ตอนนี้เราจะทำหน้าที่ฟังก์ชั่นนี้:
function add (a) {
return function (b) {
return a + b;
}
}
นี่คือฟังก์ชันที่รับอาร์กิวเมนต์หนึ่งตัวและส่งคืนฟังก์ชันที่รับอาร์กิวเมนต์อีกตัวหนึ่ง b และฟังก์ชันนั้นคืนค่าผลรวมของพวกมัน
add(3)(4);
var add3 = add(3);
add3(4);
คำสั่งแรกส่งคืน 7 เช่นคำสั่ง add (3, 4) คำสั่งที่สองกำหนดฟังก์ชั่นใหม่ที่เรียกว่า add3 ที่จะเพิ่ม 3 ลงในอาร์กิวเมนต์ของมัน นี่คือสิ่งที่บางคนอาจเรียกว่าการปิด คำสั่งที่สามใช้การดำเนินการ add3 เพื่อเพิ่ม 3 ถึง 4 สร้างผลลัพธ์ 7 อีกครั้ง
[1, 2, 3, 4, 5]
ที่คุณต้องการคูณด้วยตัวเลขใด ๆ ใน Haskell ฉันสามารถเขียนmap (* 5) [1, 2, 3, 4, 5]
เพื่อเพิ่มจำนวนรายการทั้งหมดด้วย5
และสร้างรายการ[5, 10, 15, 20, 25]
ขึ้นมา
map
ต้องเป็นฟังก์ชันที่ใช้เพียง 1 อาร์กิวเมนต์เท่านั้น - องค์ประกอบจากรายการ การคูณ - ตามแนวคิดทางคณิตศาสตร์ - เป็นการดำเนินการแบบไบนารี ใช้เวลา 2 ข้อโต้แย้ง อย่างไรก็ตามใน Haskell *
เป็นฟังก์ชั่น curried คล้ายกับเวอร์ชั่นที่สองของadd
คำตอบนี้ ผลที่ได้(* 5)
คือฟังก์ชั่นที่รับอาร์กิวเมนต์เดี่ยวและคูณด้วย 5 และทำให้เราสามารถใช้กับแผนที่ได้
ในพีชคณิตของฟังก์ชั่นการจัดการกับฟังก์ชั่นที่มีอาร์กิวเมนต์หลายตัว (หรือเทียบเท่าหนึ่งอาร์กิวเมนต์ที่เป็น N-tuple) ค่อนข้างไม่เหมาะสม - แต่ตามที่โมเสสSchönfinkel (และอิสระ Haskell Curry) พิสูจน์แล้วว่ามันไม่จำเป็น: ทั้งหมดที่คุณ ต้องการฟังก์ชั่นที่ใช้เวลาหนึ่งอาร์กิวเมนต์
ดังนั้นคุณจะจัดการกับสิ่งที่คุณแสดงออกโดยธรรมชาติว่าf(x,y)
อย่างไร ดีที่คุณใช้เวลาที่เป็นเทียบเท่ากับf(x)(y)
- f(x)
เรียกว่าเป็นฟังก์ชั่นและคุณใช้ฟังก์ชั่นที่g
y
กล่าวอีกนัยหนึ่งคุณมีเพียงฟังก์ชันที่รับหนึ่งอาร์กิวเมนต์เท่านั้น แต่บางฟังก์ชันเหล่านั้นส่งคืนฟังก์ชันอื่น ๆ (ซึ่งยังใช้อาร์กิวเมนต์ตัวเดียว ;-)
ตามปกติวิกิพีเดียมีรายการสรุปที่ดีเกี่ยวกับเรื่องนี้พร้อมด้วยพอยน์เตอร์ที่มีประโยชน์มากมาย (อาจรวมถึงภาษาที่คุณชื่นชอบ ;-) รวมถึงการรักษาทางคณิตศาสตร์ที่เข้มงวดกว่าเล็กน้อย
div :: Integral a => a -> a -> a
- สังเกตลูกศรหลายอัน? "แมป a ถึงฟังก์ชันการแมป a กับ" คือการอ่านหนึ่งเดียว คุณสามารถใช้อาร์กิวเมนต์ tuple (เดี่ยว) สำหรับdiv
& c แต่นั่นจะเป็นการต่อต้านการใช้สำนวนใน Haskell
นี่คือตัวอย่างที่เป็นรูปธรรม:
สมมติว่าคุณมีฟังก์ชันที่คำนวณแรงโน้มถ่วงที่กระทำกับวัตถุ หากคุณไม่ทราบสูตรคุณสามารถค้นหาได้ที่นี่ ฟังก์ชันนี้ใช้พารามิเตอร์ที่จำเป็นสามตัวเป็นอาร์กิวเมนต์
ตอนนี้อยู่บนโลกคุณต้องการคำนวณแรงสำหรับวัตถุบนโลกใบนี้ ในภาษาที่ใช้งานได้คุณสามารถส่งผ่านมวลของโลกไปยังฟังก์ชันแล้วประเมินบางส่วนได้ สิ่งที่คุณจะได้กลับมาคือฟังก์ชั่นอื่นที่รับเพียงสองข้อโต้แย้งและคำนวณแรงโน้มถ่วงของวัตถุบนโลก สิ่งนี้เรียกว่าการแกง
Currying คือการแปลงที่สามารถนำไปใช้กับฟังก์ชั่นเพื่อให้พวกเขาสามารถโต้แย้งน้อยลงกว่าเดิม
ตัวอย่างเช่นใน F # คุณสามารถกำหนดฟังก์ชั่นดังนี้: -
let f x y z = x + y + z
ฟังก์ชัน f ใช้พารามิเตอร์ x, y และ z และรวมเข้าด้วยกันดังนี้: -
f 1 2 3
ส่งคืน 6
จากคำจำกัดความของเราเราสามารถกำหนดฟังก์ชั่นแกงสำหรับ f: -
let curry f = fun x -> f x
โดยที่ 'fun x -> fx' คือฟังก์ชันแลมบ์ดาเทียบเท่ากับ x => f (x) ใน C # ฟังก์ชั่นนี้ใส่ฟังก์ชั่นที่คุณต้องการที่จะแกงและส่งกลับฟังก์ชั่นที่ใช้เวลาอาร์กิวเมนต์เดียวและส่งกลับฟังก์ชั่นที่ระบุด้วยการตั้งค่าอาร์กิวเมนต์แรกเป็นอาร์กิวเมนต์ป้อนเข้า
โดยใช้ตัวอย่างก่อนหน้าของเราเราสามารถได้รับแกงกะหรี่ f: -
let curryf = curry f
จากนั้นเราสามารถทำสิ่งต่อไปนี้: -
let f1 = curryf 1
ซึ่งให้ฟังก์ชัน f1 ซึ่งเทียบเท่ากับ f1 yz = 1 + y + z หมายความว่าเราสามารถทำสิ่งต่อไปนี้: -
f1 2 3
ซึ่งส่งคืน 6
กระบวนการนี้มักจะสับสนกับ 'สมัครฟังก์ชันบางส่วน' ซึ่งสามารถกำหนดได้ดังนี้: -
let papply f x = f x
แม้ว่าเราสามารถขยายไปยังพารามิเตอร์มากกว่าหนึ่งเช่น: -
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
แอปพลิเคชั่นบางส่วนจะรับฟังก์ชั่นและพารามิเตอร์และส่งคืนฟังก์ชันที่ต้องการพารามิเตอร์อย่างน้อยหนึ่งพารามิเตอร์และเนื่องจากการแสดงสองตัวอย่างก่อนหน้านี้ถูกนำไปใช้โดยตรงในการกำหนดฟังก์ชัน F # มาตรฐานเพื่อให้เราสามารถบรรลุผลก่อนหน้านี้
let f1 = f 1
f1 2 3
ซึ่งจะส่งคืนผลลัพธ์เป็น 6
สรุปแล้ว:-
ความแตกต่างระหว่างการประยุกต์ใช้ฟังก์ชั่นการแกงและบางส่วนคือ: -
Currying รับฟังก์ชั่นและจัดให้มีฟังก์ชั่นใหม่ที่รับอาร์กิวเมนต์เดียวและส่งคืนฟังก์ชันที่ระบุด้วยอาร์กิวเมนต์แรกที่ตั้งค่าเป็นอาร์กิวเมนต์นั้น นี้ช่วยให้เราเป็นตัวแทนของฟังก์ชั่นที่มีหลายพารามิเตอร์เป็นชุดของฟังก์ชั่นการโต้แย้งเดียว ตัวอย่าง:-
let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6
แอปพลิเคชั่นบางส่วนนั้นโดยตรงมากขึ้น - มันต้องใช้ฟังก์ชั่นและหนึ่งหรือมากกว่าหนึ่งข้อโต้แย้งและส่งกลับฟังก์ชั่นที่มีข้อโต้แย้งแรกที่ตั้งค่าให้อาร์กิวเมนต์ n ระบุ ตัวอย่าง:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
มันอาจเป็นวิธีการใช้ฟังก์ชั่นเพื่อสร้างฟังก์ชั่นอื่น ๆ
ในจาวาสคริปต์:
let add = function(x){
return function(y){
return x + y
};
};
จะอนุญาตให้เราเรียกมันว่าอย่างนั้น:
let addTen = add(10);
เมื่อสิ่งนี้ทำงาน10
จะถูกส่งผ่านเป็นx
;
let add = function(10){
return function(y){
return 10 + y
};
};
ซึ่งหมายความว่าเราจะส่งคืนฟังก์ชันนี้:
function(y) { return 10 + y };
ดังนั้นเมื่อคุณโทร
addTen();
คุณกำลังโทรจริงๆ:
function(y) { return 10 + y };
ดังนั้นถ้าคุณทำสิ่งนี้:
addTen(4)
มันเหมือนกับ:
function(4) { return 10 + 4} // 14
ดังนั้นเราaddTen()
มักจะเพิ่มสิบในสิ่งที่เราผ่านเราสามารถสร้างฟังก์ชันที่คล้ายกันในลักษณะเดียวกัน:
let addTwo = add(2) // addTwo(); will add two to whatever you pass in
let addSeventy = add(70) // ... and so on...
ตอนนี้คำถามติดตามที่ชัดเจนคือทำไมคุณต้องทำอย่างนั้นบนโลก? มันเปลี่ยนสิ่งที่เป็นการดำเนินการที่กระตือรือร้นx + y
เป็นสิ่งที่สามารถก้าวผ่านความเกียจคร้านซึ่งหมายความว่าเราสามารถทำอย่างน้อยสองสิ่ง 1. แคชการดำเนินการที่มีราคาแพง 2. บรรลุ abstractions ในกระบวนทัศน์การทำงาน
ลองนึกภาพฟังก์ชั่น curried ของเราเป็นดังนี้:
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
เราสามารถเรียกใช้ฟังก์ชั่นนี้ครั้งเดียวจากนั้นส่งผ่านผลลัพธ์ที่จะใช้ในหลาย ๆ สถานที่ซึ่งหมายความว่าเราจะทำสิ่งที่มีราคาแพงเพียงครั้งเดียว:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
เราสามารถรับ abstractions ในทำนองเดียวกัน
ฟังก์ชั่น curried เป็นฟังก์ชั่นของข้อโต้แย้งหลายข้อที่เขียนใหม่เพื่อให้ยอมรับอาร์กิวเมนต์แรกและส่งกลับฟังก์ชันที่ยอมรับอาร์กิวเมนต์ที่สองเป็นต้น สิ่งนี้อนุญาตให้ฟังก์ชันของอาร์กิวเมนต์หลายตัวมีข้อโต้แย้งเริ่มต้นบางส่วนที่นำมาใช้บางส่วน
map
ฟังก์ชั่นf
มากกว่ารายการแสดงรายการที่คุณสามารถทำได้xss
map (map f) xss
นี่คือตัวอย่างของเล่นใน Python:
>>> from functools import partial as curry
>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
print who, 'said regarding', subject + ':'
print '"' + quote + '"'
>>> display_quote("hoohoo", "functional languages",
"I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."
>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")
>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."
(เพียงใช้การต่อข้อมูลผ่าน + เพื่อหลีกเลี่ยงสิ่งรบกวนสำหรับโปรแกรมเมอร์ที่ไม่ใช่ Python)
การแก้ไขเพื่อเพิ่ม:
ดูที่http://docs.python.org/library/functools.html?highlight=partial#functools.partialซึ่งแสดงวัตถุบางส่วนเทียบกับความแตกต่างของฟังก์ชันในวิธีที่ Python ใช้สิ่งนี้
ความดีความชอบเป็นฟังก์ชั่นแปลจาก callable เป็นf(a, b, c)
เข้า callable f(a)(b)(c)
เป็น
มิฉะนั้นการปิดกั้นคือเมื่อคุณแยกฟังก์ชั่นที่ใช้หลาย ๆ อาร์กิวเมนต์ลงในชุดของฟังก์ชันที่มีส่วนร่วมของอาร์กิวเมนต์
แท้จริงแล้วการแกงเป็นการเปลี่ยนแปลงของฟังก์ชั่น: จากวิธีหนึ่งในการโทรไปยังอีกวิธีหนึ่ง ใน JavaScript เรามักจะทำ wrapper เพื่อคงฟังก์ชั่นดั้งเดิมเอาไว้
การแกงไม่เรียกฟังก์ชั่น มันแค่แปลงมัน
ลองสร้างฟังก์ชั่นการแกงที่ทำหน้าที่แกงสำหรับสองข้อโต้แย้ง กล่าวอีกนัยหนึ่งcurry(f)
สำหรับสองข้อโต้แย้งf(a, b)
แปลมันเป็นf(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
อย่างที่คุณเห็นการใช้งานเป็นชุดของตัวห่อ
curry(func)
เป็น wrapperfunction(a)
เป็นเสื้อคลุมsum(1)
อาร์กิวเมนต์จะถูกบันทึกใน Lexical Environment และจะส่งคืน wrapper ใหม่function(b)
อาร์กิวเมนต์จะถูกบันทึกไว้ในคำศัพท์สิ่งแวดล้อมและเสื้อคลุมใหม่จะถูกส่งกลับsum(1)(2)
เรียกfunction(b)
การจัดเตรียม 2 และผ่านการเรียกไปยังผลรวมของอาร์กิวเมนต์หลายตัวดั้งเดิมหากคุณเข้าใจว่าpartial
คุณอยู่ตรงนั้น ความคิดของpartial
การใช้อาร์กิวเมนต์ล่วงหน้าไปยังฟังก์ชันและส่งคืนฟังก์ชันใหม่ที่ต้องการเฉพาะอาร์กิวเมนต์ที่เหลือ เมื่อฟังก์ชั่นใหม่นี้เรียกว่ามันจะรวมถึงการขัดแย้งที่โหลดไว้ล่วงหน้าพร้อมกับสิ่งที่ข้อโต้แย้งใด ๆ ที่ได้รับมัน
ใน Clojure +
เป็นฟังก์ชั่น แต่จะทำให้สิ่งต่าง ๆ ชัดเจน:
(defn add [a b] (+ a b))
คุณอาจทราบว่าinc
ฟังก์ชั่นนั้นเพิ่ม 1 เข้าไปในหมายเลขใด ๆ ที่มันผ่าน
(inc 7) # => 8
มาสร้างมันเองโดยใช้partial
:
(def inc (partial add 1))
ที่นี่เรากลับฟังก์ชั่นที่ได้ 1 add
โหลดลงในอาร์กิวเมนต์แรกของ ในฐานะที่เป็นadd
เวลาสองขัดแย้งใหม่inc
ฟังก์ชั่นที่ต้องการเพียงb
การโต้แย้ง - ไม่ 2 ข้อโต้แย้งเป็นมาก่อนตั้งแต่วันที่ 1 ได้รับแล้วบางส่วนนำไปใช้ ดังนั้นจึงpartial
เป็นเครื่องมือในการสร้างฟังก์ชั่นใหม่ที่มีการกำหนดค่าเริ่มต้นไว้ล่วงหน้า นั่นคือเหตุผลที่ฟังก์ชั่นภาษาที่ใช้งานได้มักจะเรียงลำดับอาร์กิวเมนต์จากทั่วไปถึงเฉพาะ สิ่งนี้ทำให้ง่ายต่อการใช้ฟังก์ชั่นดังกล่าวเพื่อสร้างฟังก์ชั่นอื่น ๆ
ทีนี้ลองคิดดูว่าภาษานั้นฉลาดพอที่จะเข้าใจการใคร่ครวญที่add
ต้องการข้อโต้แย้งสองข้อหรือไม่ เมื่อเราผ่านการโต้แย้งหนึ่งไปแล้วแทนที่จะขัดจังหวะจะเกิดอะไรขึ้นถ้าฟังก์ชั่นใช้การโต้เถียงบางส่วนที่เราส่งผ่านมันไปแทนความเข้าใจของเราว่าเราอาจหมายถึงการโต้เถียงอีกครั้งในภายหลัง จากนั้นเราจะสามารถกำหนดได้โดยไม่ต้องใช้อย่างชัดเจนinc
partial
(def inc (add 1)) #partial is implied
นี่คือวิธีการทำงานของบางภาษา มันมีประโยชน์เป็นพิเศษเมื่อมีใครต้องการที่จะเขียนฟังก์ชั่นในการแปลงขนาดใหญ่ สิ่งนี้จะนำไปสู่การเปลี่ยนแปลง
ฉันพบบทความนี้และบทความที่อ้างอิงมีประโยชน์เพื่อทำความเข้าใจกับการแกง: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-application.aspx
ดังที่คนอื่น ๆ กล่าวถึงมันเป็นเพียงวิธีหนึ่งที่จะมีฟังก์ชั่นพารามิเตอร์เดียว
สิ่งนี้มีประโยชน์ในกรณีที่คุณไม่จำเป็นต้องสมมติว่ามีพารามิเตอร์กี่ตัวที่จะถูกส่งผ่านดังนั้นคุณไม่จำเป็นต้องใช้ฟังก์ชัน 2 พารามิเตอร์ 3 พารามิเตอร์และ 4 พารามิเตอร์
ในฐานะที่เป็นคำตอบอื่น ๆ ทั้งหมด currying ช่วยในการสร้างฟังก์ชั่นการใช้งานบางส่วน Javascript ไม่ได้ให้การสนับสนุนดั้งเดิมสำหรับการแกงอัตโนมัติ ตัวอย่างที่ให้ไว้ข้างต้นอาจไม่ช่วยในการเขียนโปรแกรมเชิงปฏิบัติ มีตัวอย่างที่ยอดเยี่ยมบางส่วนใน livescript (ซึ่งเป็นหลักในการรวบรวม js) http://livescript.net/
times = (x, y) --> x * y
times 2, 3 #=> 6 (normal use works as expected)
double = times 2
double 5 #=> 10
ในตัวอย่างด้านบนเมื่อคุณได้รับข้อโต้แย้งไลฟ์สทริบิวชั่นน้อยสร้างฟังก์ชัน curried ใหม่สำหรับคุณ
แกงสามารถทำให้รหัสของคุณง่ายขึ้น นี่คือหนึ่งในเหตุผลหลักที่ใช้สิ่งนี้ Currying เป็นกระบวนการแปลงฟังก์ชั่นที่ยอมรับอาร์กิวเมนต์ n เป็นฟังก์ชัน n ที่ยอมรับเพียงหนึ่งอาร์กิวเมนต์
หลักการคือการส่งผ่านข้อโต้แย้งของฟังก์ชั่นที่ส่งผ่านโดยใช้คุณสมบัติการปิด (ปิด) เพื่อเก็บไว้ในฟังก์ชั่นอื่นและถือว่ามันเป็นค่าตอบแทนและฟังก์ชั่นเหล่านี้ในรูปแบบห่วงโซ่และอาร์กิวเมนต์สุดท้ายจะผ่าน การดำเนินการ
ประโยชน์ของสิ่งนี้คือมันสามารถทำให้การประมวลผลพารามิเตอร์ง่ายขึ้นโดยจัดการกับพารามิเตอร์หนึ่งตัวในแต่ละครั้งซึ่งยังสามารถปรับปรุงความยืดหยุ่นและความสามารถในการอ่านของโปรแกรม นอกจากนี้ยังทำให้โปรแกรมสามารถจัดการได้มากขึ้น การแบ่งรหัสออกเป็นส่วนย่อย ๆ จะทำให้การนำมาใช้ซ้ำได้ง่ายขึ้น
ตัวอย่างเช่น:
function curryMinus(x)
{
return function(y)
{
return x - y;
}
}
var minus5 = curryMinus(1);
minus5(3);
minus5(5);
ฉันสามารถทำ ...
var minus7 = curryMinus(7);
minus7(3);
minus7(5);
สิ่งนี้ยอดเยี่ยมมากสำหรับการทำโค้ดที่ซับซ้อนให้เรียบร้อยและการจัดการกับวิธีการที่ไม่ซิงโครไนซ์เป็นต้น
ฟังก์ชั่น curried ถูกนำไปใช้กับรายการอาร์กิวเมนต์หลายรายการแทนที่จะเป็นเพียงรายการเดียว
นี่คือฟังก์ชั่นปกติที่ไม่ใช่ curried ซึ่งเพิ่มพารามิเตอร์ Int สองตัวคือ x และ y:
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3
นี่คือฟังก์ชั่นที่คล้ายกันที่ curried แทนที่จะใช้หนึ่งรายการของสองพารามิเตอร์ Int คุณสามารถใช้ฟังก์ชันนี้กับสองรายการของหนึ่งพารามิเตอร์ Int แต่ละตัว:
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3
สิ่งที่เกิดขึ้นที่นี่คือเมื่อคุณเรียกใช้curriedSum
คุณจะได้รับการเรียกใช้ฟังก์ชันดั้งเดิมสองรายการกลับไปด้านหลัง การเรียกใช้ฟังก์ชันแรกใช้พารามิเตอร์ Int เดียวที่มีชื่อx
และส่งคืนค่าฟังก์ชันสำหรับฟังก์ชันที่สอง y
ฟังก์ชั่นที่สองนี้จะใช้เวลาพารามิเตอร์ Int
นี่คือฟังก์ชั่นที่ตั้งชื่อโดยfirst
คำนึงถึงสิ่งที่การเรียกใช้ฟังก์ชันดั้งเดิมครั้งแรกcurriedSum
จะทำ:
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
การนำ 1 ไปใช้กับฟังก์ชันแรก - กล่าวอีกนัยหนึ่งคือเรียกใช้ฟังก์ชันแรกและผ่านไปใน 1 - ให้ฟังก์ชันที่สอง:
scala> val second = first(1)
second: (Int) => Int = <function1>
การใช้ 2 กับฟังก์ชันที่สองจะให้ผลลัพธ์:
scala> second(2)
res6: Int = 3
ตัวอย่างของการ currying คือเมื่อมีฟังก์ชั่นที่คุณรู้เพียงหนึ่งในพารามิเตอร์ในขณะนี้:
ตัวอย่างเช่น:
func aFunction(str: String) {
let callback = callback(str) // signature now is `NSData -> ()`
performAsyncRequest(callback)
}
func callback(str: String, data: NSData) {
// Callback code
}
func performAsyncRequest(callback: NSData -> ()) {
// Async code that will call callback with NSData as parameter
}
ที่นี่เนื่องจากคุณไม่ทราบพารามิเตอร์ที่สองสำหรับการโทรกลับเมื่อส่งถึงperformAsyncRequest(_:)
คุณคุณจะต้องสร้างแลมบ์ดา / ปิดอีกอันเพื่อส่งหนึ่งไปยังฟังก์ชัน
func callback
กลับมา? มันถูกเรียกว่า @ callback(str)
ดังนั้นการlet callback = callback(str)
เรียกกลับเป็นเพียงค่าตอบแทนของfunc callback
func callback(_:data:)
ยอมรับพารามิเตอร์สองตัวที่นี่ฉันให้มันเพียงอันเดียวString
ดังนั้นมันจึงรออีกอัน ( NSData
) นี่คือเหตุผลว่าทำไมตอนนี้let callback
ฟังก์ชั่นอื่นกำลังรอข้อมูลที่จะส่งผ่าน
นี่คือตัวอย่างของเวอร์ชันทั่วไปและเวอร์ชันที่สั้นที่สุดสำหรับฟังก์ชัน currying ที่ไม่มี n ของ params
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
ที่นี่คุณจะพบคำอธิบายง่ายๆเกี่ยวกับการใช้งาน currying ใน C # ในความคิดเห็นฉันพยายามแสดงให้เห็นว่าการแกงมีประโยชน์อย่างไร:
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
Currying เป็นหนึ่งในฟังก์ชันลำดับสูงของ Java Script
Currying เป็นฟังก์ชั่นของอาร์กิวเมนต์จำนวนมากซึ่งถูกเขียนใหม่เพื่อให้ได้อาร์กิวเมนต์แรกและส่งคืนฟังก์ชันที่ใช้อาร์กิวเมนต์ที่เหลือและส่งกลับค่า
สับสน?
ลองดูตัวอย่าง
function add(a,b)
{
return a+b;
}
add(5,6);
คล้ายกับฟังก์ชั่นการแกงต่อไปนี้
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
ดังนั้นรหัสนี้หมายความว่าอย่างไร
ตอนนี้อ่านคำจำกัดความอีกครั้ง
Currying เป็นฟังก์ชั่นของอาร์กิวเมนต์จำนวนมากซึ่งถูกเขียนใหม่เพื่อให้ได้อาร์กิวเมนต์แรกและส่งคืนฟังก์ชันซึ่งจะใช้อาร์กิวเมนต์ที่เหลือและส่งกลับค่า
ยังสับสนอยู่ไหม? ให้ฉันอธิบายอย่างลึกซึ้ง!
เมื่อคุณเรียกใช้ฟังก์ชันนี้
var curryAdd = add(5);
มันจะคืนค่าฟังก์ชันเช่นนี้ให้กับคุณ
curryAdd=function(y){return 5+y;}
ดังนั้นนี่เรียกว่าฟังก์ชันลำดับที่สูงกว่า ความหมายการเรียกใช้ฟังก์ชั่นหนึ่งในผลตอบแทนที่ส่งกลับฟังก์ชั่นอื่นเป็นคำนิยามที่แน่นอนสำหรับฟังก์ชั่นการสั่งซื้อที่สูงขึ้น นี่เป็นข้อได้เปรียบที่ยิ่งใหญ่ที่สุดสำหรับ Java Script ดังนั้นกลับมาที่การแกง
บรรทัดนี้จะส่งผ่านอาร์กิวเมนต์ที่สองไปยังฟังก์ชัน curryAdd
curryAdd(6);
ซึ่งจะส่งผลให้
curryAdd=function(6){return 5+6;}
// Which results in 11
หวังว่าคุณจะเข้าใจการใช้งานของแกงที่นี่ ดังนั้นมาถึงข้อดี
ทำไมต้องแกง
มันทำให้การใช้งานของรหัสนำมาใช้ รหัสน้อยลง, ข้อผิดพลาดน้อยลง คุณอาจถามว่ามันเป็นรหัสน้อยได้อย่างไร
ฉันสามารถพิสูจน์ได้ด้วยฟังก์ชันลูกศรคุณลักษณะใหม่ของ ECMA script 6
ใช่ ECMA 6 มอบคุณลักษณะที่ยอดเยี่ยมที่เรียกว่าฟังก์ชั่นลูกศร
function add(a)
{
return function(b){
return a+b;
}
}
ด้วยความช่วยเหลือของฟังก์ชั่นลูกศรเราสามารถเขียนฟังก์ชั่นด้านบนดังนี้
x=>y=>x+y
เจ๋งใช่มั้ย
ดังนั้นรหัสน้อยและข้อบกพร่องน้อยลง !!
ด้วยความช่วยเหลือของฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเหล่านี้คุณสามารถพัฒนาโค้ดที่ปราศจากข้อบกพร่องได้อย่างง่ายดาย
ฉันขอท้าคุณ!
หวังว่าคุณเข้าใจสิ่งที่กำลังร้องไห้ โปรดแสดงความคิดเห็นที่นี่หากคุณต้องการคำชี้แจงใด ๆ
ขอบคุณมีวันที่ดี!
มีตัวอย่างของ "Currying in ReasonML"
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
และuncurry
หน้าที่ของ Haskell สิ่งสำคัญคือที่นี่ isomorphisms เหล่านี้ได้รับการแก้ไขล่วงหน้าและดังนั้น "ในตัว" เป็นภาษา