ฉันได้อ่านบทความ Wikipedia สำหรับทั้งขั้นตอนการเขียนโปรแกรมและการเขียนโปรแกรมเพื่อการใช้งาน แต่ฉันยังสับสนอยู่เล็กน้อย ใครบางคนสามารถต้มมันลงไปที่แกนกลางได้หรือไม่?
ฉันได้อ่านบทความ Wikipedia สำหรับทั้งขั้นตอนการเขียนโปรแกรมและการเขียนโปรแกรมเพื่อการใช้งาน แต่ฉันยังสับสนอยู่เล็กน้อย ใครบางคนสามารถต้มมันลงไปที่แกนกลางได้หรือไม่?
คำตอบ:
ฟังก์ชั่นภาษา (นึกคิด) ช่วยให้คุณสามารถเขียนฟังก์ชั่นทางคณิตศาสตร์เช่นฟังก์ชั่นที่ใช้เวลาอาร์กิวเมนต์nและส่งกลับค่า หากโปรแกรมถูกดำเนินการฟังก์ชันนี้จะได้รับการประเมินอย่างมีเหตุผลตามต้องการ 1
ในทางกลับกันภาษาขั้นตอนจะดำเนินการตามลำดับขั้นตอน (มีวิธีการแปลงตรรกะตามลำดับเป็นตรรกะการทำงานที่เรียกว่ารูปแบบการส่งต่อ )
ด้วยเหตุนี้โปรแกรมที่ใช้งานได้อย่างแท้จริงจะให้คุณค่าที่เหมือนกันสำหรับอินพุตและลำดับของการประเมินไม่ชัดเจน ซึ่งหมายความว่าค่าที่ไม่แน่นอนเช่นการป้อนข้อมูลของผู้ใช้หรือค่าสุ่มนั้นยากที่จะสร้างแบบจำลองในภาษาที่ใช้งานได้จริง
1ทุกอย่างในคำตอบนี้เป็นลักษณะทั่วไป คุณสมบัตินี้ประเมินการคำนวณเมื่อต้องการผลลัพธ์แทนที่จะเรียงตามลำดับที่เรียกว่าเรียกว่า "ความเกียจคร้าน" ไม่ใช่ทุกภาษาที่ใช้งานได้จริงเป็นคนขี้เกียจในระดับสากล แต่คำอธิบายที่ให้ไว้ที่นี่ให้“ กรอบงานทางจิต” เพื่อคิดเกี่ยวกับรูปแบบการเขียนโปรแกรมที่แตกต่างกันซึ่งไม่ได้แยกประเภทและแตกต่างกัน
โดยทั่วไปทั้งสองรูปแบบเหมือนหยินและหยาง หนึ่งถูกจัดระเบียบในขณะที่อื่น ๆ ที่วุ่นวาย มีสถานการณ์ที่เมื่อการตั้งโปรแกรมฟังก์ชั่นเป็นตัวเลือกที่ชัดเจนและสถานการณ์อื่น ๆ คือการเขียนโปรแกรมตามขั้นตอนเป็นตัวเลือกที่ดีกว่า นี่คือสาเหตุที่มีภาษาอย่างน้อยสองภาษาที่เพิ่งออกมาพร้อมกับเวอร์ชันใหม่ซึ่งรวบรวมสไตล์การเขียนโปรแกรมทั้งสองไว้ ( Perl 6และD 2 )
sub factorial ( UInt:D $n is copy ) returns UInt {
# modify "outside" state
state $call-count++;
# in this case it is rather pointless as
# it can't even be accessed from outside
my $result = 1;
loop ( ; $n > 0 ; $n-- ){
$result *= $n;
}
return $result;
}
int factorial( int n ){
int result = 1;
for( ; n > 0 ; n-- ){
result *= n;
}
return result;
}
(คัดลอกมาจากWikipedia );
fac :: Integer -> Integer
fac 0 = 1
fac n | n > 0 = n * fac (n-1)
หรือในหนึ่งบรรทัด:
fac n = if n > 0 then n * fac (n-1) else 1
proto sub factorial ( UInt:D $n ) returns UInt {*}
multi sub factorial ( 0 ) { 1 }
multi sub factorial ( $n ) { $n * samewith $n-1 } # { $n * factorial $n-1 }
pure int factorial( invariant int n ){
if( n <= 1 ){
return 1;
}else{
return n * factorial( n-1 );
}
}
แฟคทอเรียลเป็นตัวอย่างทั่วไปที่แสดงให้เห็นว่าการสร้างโอเปอเรเตอร์ใหม่ใน Perl 6 นั้นง่ายเพียงใดเช่นเดียวกับที่คุณสร้างรูทีนย่อย คุณลักษณะนี้ฝังอยู่ใน Perl 6 ซึ่งผู้ให้บริการส่วนใหญ่ในการนำ Rakudo ไปใช้จะถูกกำหนดด้วยวิธีนี้ นอกจากนี้ยังช่วยให้คุณเพิ่มผู้สมัครหลายคนของคุณเองไปยังผู้ให้บริการที่มีอยู่
sub postfix:< ! > ( UInt:D $n --> UInt )
is tighter(&infix:<*>)
{ [*] 2 .. $n }
say 5!; # 120
ตัวอย่างนี้ยังแสดงการสร้างช่วง ( 2..$n
) และการลดรายการ meta-operator ( [ OPERATOR ] LIST
) รวมกับตัวดำเนินการคูณตัวเลข infix ( *
)
นอกจากนี้ยังแสดงว่าคุณสามารถใส่--> UInt
ลายเซ็นแทนreturns UInt
หลังจากนั้น
(คุณสามารถหนีโดยเริ่มต้นช่วงด้วย2
เพราะทวีคูณ "โอเปอเรเตอร์" จะกลับมา1
เมื่อถูกเรียกโดยไม่มีข้อโต้แย้งใด ๆ )
sub postfix:<!> ($n) { [*] 1..$n }
No operation can have side effects
- คุณช่วยอธิบายได้ไหม?
sub foo( $a, $b ){ ($a,$b).pick }
←ไม่คืนค่าผลลัพธ์เดียวกันสำหรับอินพุตเดียวกันเสมอในขณะที่สิ่งต่อไปนี้จะต้องทำsub foo( $a, $b ){ $a + $b }
ฉันไม่เคยเห็นคำนิยามนี้ให้ที่อื่น แต่ฉันคิดว่านี่สรุปความแตกต่างที่ได้รับที่นี่ค่อนข้างดี:
ฟังก์ชันการเขียนโปรแกรมมุ่งเน้นไปที่การแสดงออก
การเขียนโปรแกรมตามขั้นตอนจะเน้นไปที่ข้อความ
นิพจน์มีค่า โปรแกรมที่ใช้งานได้คือนิพจน์ที่มีค่าเป็นลำดับของคำสั่งสำหรับคอมพิวเตอร์ที่จะดำเนินการ
คำสั่งไม่มีค่าและปรับเปลี่ยนสถานะของเครื่องแนวคิดบางอย่างแทน
ในภาษาที่ใช้งานได้จริงจะไม่มีข้อความใด ๆ ในแง่ที่ว่าไม่มีทางจัดการกับสถานะ (พวกเขายังอาจมีโครงสร้างประโยคคำว่า "ประโยค" แต่ถ้ามันจัดการกับสถานะฉันจะไม่เรียกมันว่าข้อความในแง่นี้ ) ในภาษาขั้นตอนอย่างหมดจดจะไม่มีการแสดงออกทุกอย่างจะเป็นคำสั่งที่จัดการกับสถานะของเครื่อง
Haskell จะเป็นตัวอย่างของภาษาที่ใช้งานได้อย่างหมดจดเพราะไม่มีวิธีจัดการกับรัฐ รหัสเครื่องจะเป็นตัวอย่างของภาษาขั้นตอนอย่างหมดจดเพราะทุกอย่างในโปรแกรมเป็นคำสั่งที่จัดการกับสถานะของการลงทะเบียนและหน่วยความจำของเครื่อง
ส่วนที่สับสนคือภาษาโปรแกรมส่วนใหญ่มีทั้งสองอย่างการแสดงออกและข้อความซึ่งช่วยให้คุณสามารถผสมกระบวนทัศน์ ภาษาสามารถแบ่งได้เป็นฟังก์ชันที่มีประโยชน์มากกว่าหรือมีขั้นตอนมากขึ้นโดยขึ้นอยู่กับว่าพวกเขาส่งเสริมการใช้ข้อความเทียบกับนิพจน์
ตัวอย่างเช่น C จะทำงานได้มากกว่าภาษาโคบอลเพราะการเรียกใช้ฟังก์ชั่นเป็นการแสดงออกในขณะที่เรียกโปรแกรมย่อยในภาษาโคบอลเป็นคำสั่ง (ที่จัดการกับสถานะของตัวแปรที่แชร์และไม่ส่งคืนค่า) Python จะทำงานได้ดีกว่า C เพราะช่วยให้คุณสามารถแสดงตรรกะตามเงื่อนไขในฐานะนิพจน์โดยใช้การประเมินผลแบบลัดวงจร (ทดสอบ && path1 || path2 ซึ่งตรงกันข้ามกับคำสั่ง if) โครงการจะทำงานได้ดีกว่า Python เพราะทุกอย่างในโครงร่างนั้นเป็นนิพจน์
คุณยังสามารถเขียนในรูปแบบการใช้งานได้ในภาษาที่สนับสนุนกระบวนทัศน์กระบวนงานและในทางกลับกัน มันยากขึ้นและ / หรืออึดอัดใจกว่าที่จะเขียนในกระบวนทัศน์ที่ไม่ได้รับการสนับสนุนจากภาษา
ในวิทยาการคอมพิวเตอร์การเขียนโปรแกรมเชิงการทำงานเป็นกระบวนทัศน์การเขียนโปรแกรมที่ปฏิบัติกับการคำนวณเป็นการประเมินฟังก์ชั่นทางคณิตศาสตร์ มันเน้นการประยุกต์ใช้ฟังก์ชั่นในทางตรงกันข้ามกับรูปแบบการเขียนโปรแกรมขั้นตอนที่เน้นการเปลี่ยนแปลงในรัฐ
GetUserContext()
ในฟังก์ชั่นบริบทของผู้ใช้จะถูกส่งผ่านมานี่คือการเขียนโปรแกรมการทำงานหรือไม่ ขอบคุณล่วงหน้า.
num = 1
def function_to_add_one(num):
num += 1
return num
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
#Final Output: 2
num = 1
def procedure_to_add_one():
global num
num += 1
return num
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
#Final Output: 6
function_to_add_one
เป็นฟังก์ชั่น
procedure_to_add_one
เป็นขั้นตอน
แม้ว่าคุณจะใช้ฟังก์ชั่นห้าครั้งทุกครั้งที่มันกลับมา2
ถ้าคุณเรียกใช้ขั้นตอนที่ห้าครั้งในตอนท้ายของการทำงานที่ห้ามันจะทำให้คุณ6
ฉันเชื่อว่าการเขียนโปรแกรมตามขั้นตอน / หน้าที่ / วัตถุประสงค์นั้นเกี่ยวกับวิธีจัดการกับปัญหา
สไตล์แรกจะวางแผนทุกอย่างเป็นขั้นตอนและแก้ไขปัญหาโดยการดำเนินการทีละขั้นตอน (ขั้นตอน) ในแต่ละครั้ง ในทางกลับกันการเขียนโปรแกรมเชิงฟังก์ชันจะเน้นวิธีการแบ่งและเอาชนะซึ่งปัญหาแบ่งออกเป็นปัญหาย่อยจากนั้นแต่ละปัญหาย่อยจะได้รับการแก้ไข (การสร้างฟังก์ชันเพื่อแก้ปัญหาย่อยนั้น) และผลลัพธ์จะถูกรวมเข้าด้วยกัน สร้างคำตอบสำหรับปัญหาทั้งหมด สุดท้ายการเขียนโปรแกรมเชิงวัตถุจะเลียนแบบโลกแห่งความจริงด้วยการสร้างโลกจำลองขนาดเล็กภายในคอมพิวเตอร์ที่มีวัตถุจำนวนมากซึ่งแต่ละอันมีลักษณะเฉพาะที่ไม่เหมือนใครและโต้ตอบกับผู้อื่น จากการโต้ตอบเหล่านั้นผลลัพธ์จะออกมา
รูปแบบการเขียนโปรแกรมแต่ละแบบมีข้อดีและจุดอ่อนของตนเอง ดังนั้นการทำบางสิ่งบางอย่างเช่น "การเขียนโปรแกรมบริสุทธิ์" (เช่นขั้นตอนอย่างหมดจด - ไม่มีใครทำสิ่งนี้โดยวิธีการซึ่งเป็นชนิดแปลก - หรือการทำงานอย่างหมดจดหรือวัตถุประสงค์อย่างหมดจด) เป็นเรื่องยากมากถ้าไม่เป็นไปไม่ได้ยกเว้นปัญหาเบื้องต้น ออกแบบมาเพื่อแสดงให้เห็นถึงประโยชน์ของรูปแบบการเขียนโปรแกรม (ดังนั้นเราจึงเรียกผู้ที่ชอบความบริสุทธิ์ "weenie": D)
จากรูปแบบเหล่านั้นเรามีภาษาการเขียนโปรแกรมที่ออกแบบมาเพื่อปรับให้เหมาะสมสำหรับแต่ละสไตล์ ตัวอย่างเช่นการชุมนุมเป็นเรื่องเกี่ยวกับขั้นตอน โอเคภาษาขั้นต้นส่วนใหญ่เป็นขั้นตอนไม่ใช่แค่ Asm เช่น C, Pascal (และ Fortran ฉันได้ยิน) จากนั้นเรามี Java ที่มีชื่อเสียงทั้งหมดในโรงเรียนวัตถุประสงค์ (ที่จริงแล้ว Java และ C # นั้นยังอยู่ในชั้นเรียนที่เรียกว่า "money-oriented" แต่นั่นก็เป็นหัวข้อสำหรับการอภิปรายอื่น) วัตถุประสงค์ก็คือ Smalltalk ในโรงเรียนที่ใช้งานได้เราจะมี "การทำงานที่เกือบจะ" (บางคนคิดว่าพวกเขาไม่บริสุทธิ์) ครอบครัว Lisp และ ML และอีกหลายคน "Haskell, Erlang และอื่น ๆ อีกมากมาย" อย่างไรก็ตามมีหลายภาษาทั่วไปเช่น Perl, Python ทับทิม
หากต้องการขยายความคิดเห็นของ Konrad:
ด้วยเหตุนี้โปรแกรมที่ใช้งานได้อย่างแท้จริงจะให้คุณค่าที่เหมือนกันสำหรับอินพุตและลำดับของการประเมินไม่ชัดเจน
ด้วยเหตุนี้รหัสการทำงานโดยทั่วไปจะง่ายต่อการขนาน เนื่องจากมี (โดยทั่วไป) ไม่มีผลข้างเคียงของฟังก์ชั่นและพวกเขา (โดยทั่วไป) เพียงแค่ดำเนินการตามข้อโต้แย้งของพวกเขาปัญหาจำนวนมากที่เกิดขึ้นพร้อมกันหายไป
การเขียนโปรแกรมฟังก์ชั่นยังใช้เมื่อคุณจำเป็นต้องมีความสามารถในการพิสูจน์รหัสของคุณถูกต้อง นี่เป็นเรื่องยากที่จะทำกับการเขียนโปรแกรมตามขั้นตอน (ไม่ใช่เรื่องง่ายกับการใช้งาน แต่ยังง่ายกว่า)
คำเตือน: ฉันไม่ได้ใช้การเขียนโปรแกรมการทำงานในปีและเพิ่งเริ่มมองมันอีกครั้งดังนั้นฉันอาจไม่ถูกต้องที่นี่ :)
สิ่งหนึ่งที่ฉันไม่เห็นจริง ๆ เน้นที่นี่คือภาษาการทำงานที่ทันสมัยเช่น Haskell มากขึ้นในฟังก์ชั่นชั้นหนึ่งสำหรับการควบคุมการไหลกว่าการสอบถามซ้ำ คุณไม่จำเป็นต้องกำหนดแฟคทอเรียลแบบเรียกซ้ำใน Haskell ดังที่ได้กล่าวมา ฉันคิดว่าชอบ
fac n = foldr (*) 1 [1..n]
เป็นการสร้างสำนวนที่สมบูรณ์แบบและมีความใกล้ชิดกับการใช้ลูปมากกว่าการใช้การเรียกซ้ำแบบชัดแจ้ง
การโปรแกรมเชิงฟังก์ชันนั้นเหมือนกับการโปรแกรมแบบโพรซีเดอร์ซึ่งไม่ได้ใช้ตัวแปรโกลบอล
ภาษาเชิงโพรซีเดอร์มีแนวโน้มที่จะติดตามสถานะ (ใช้ตัวแปร) และมีแนวโน้มที่จะดำเนินการตามลำดับขั้นตอน ภาษาที่ใช้งานได้จริงไม่ได้ติดตามสถานะใช้ค่าที่ไม่เปลี่ยนรูปแบบและมีแนวโน้มที่จะดำเนินการเป็นชุดของการอ้างอิง ในหลายกรณีสถานะของ call stack จะเก็บข้อมูลที่จะเทียบเท่ากับสิ่งที่จะถูกเก็บไว้ในตัวแปรสถานะในรหัสขั้นตอน
การเรียกซ้ำเป็นตัวอย่างคลาสสิกของการเขียนโปรแกรมสไตล์การใช้งาน
คอนราดกล่าวว่า:
ด้วยเหตุนี้โปรแกรมที่ใช้งานได้อย่างแท้จริงจะให้คุณค่าที่เหมือนกันสำหรับอินพุตและลำดับของการประเมินไม่ชัดเจน ซึ่งหมายความว่าค่าที่ไม่แน่นอนเช่นการป้อนข้อมูลของผู้ใช้หรือค่าสุ่มนั้นยากที่จะสร้างแบบจำลองในภาษาที่ใช้งานได้จริง
ลำดับของการประเมินในโปรแกรมที่ใช้งานได้จริงอาจยาก (โดยเฉพาะอย่างยิ่งกับความเกียจคร้าน) หรือแม้แต่ไม่สำคัญ แต่ฉันคิดว่าการบอกว่ามันไม่ได้นิยามไว้อย่างชัดเจนทำให้ดูเหมือนคุณไม่สามารถบอกได้ว่าโปรแกรมของคุณกำลังจะไป ไปทำงานเลย!
บางทีคำอธิบายที่ดีกว่าอาจเป็นไปได้ว่าโฟลว์การควบคุมในโปรแกรมการทำงานนั้นขึ้นอยู่กับว่าจำเป็นต้องใช้ค่าของอาร์กิวเมนต์ของฟังก์ชันหรือไม่ สิ่งที่ดีเกี่ยวกับเรื่องนี้ว่าในโปรแกรมที่เขียนอย่างดีสถานะจะชัดเจน: แต่ละฟังก์ชั่นแสดงรายการอินพุตเป็นพารามิเตอร์แทนที่จะเป็นสภาวะโลกโดยพลการ ดังนั้นในระดับบางอย่างมันเป็นเรื่องง่ายที่จะเหตุผลเกี่ยวกับคำสั่งของการประเมินผลที่เกี่ยวกับฟังก์ชั่นได้ตลอดเวลา แต่ละฟังก์ชั่นสามารถเพิกเฉยต่อส่วนที่เหลือของจักรวาลและมุ่งเน้นไปที่สิ่งที่มันต้องทำ เมื่อรวมเข้าด้วยกันฟังก์ชั่นนั้นรับประกันว่าจะทำงานเหมือนกัน [1] เหมือนกับที่มันแยกกัน
... ค่าที่ไม่แน่นอนเช่นการป้อนข้อมูลของผู้ใช้หรือค่าสุ่มยากที่จะสร้างแบบจำลองในภาษาที่ใช้งานได้จริง
วิธีการแก้ปัญหาการป้อนข้อมูลในโปรแกรมที่ทำงานได้อย่างหมดจดคือการฝังภาษาที่จำเป็นในฐานะDSLโดยใช้นามธรรมที่ทรงพลังเพียงพอนามธรรมที่มีประสิทธิภาพเพียงพอในภาษาที่จำเป็น (หรือฟังก์ชันที่ไม่บริสุทธิ์) สิ่งนี้ไม่จำเป็นเพราะคุณสามารถ "โกง" และผ่านสถานะโดยปริยายและลำดับการประเมินนั้นชัดเจน (ไม่ว่าคุณจะชอบหรือไม่ก็ตาม) ด้วยเหตุนี้ "การโกง" และการประเมินผลบังคับของพารามิเตอร์ทั้งหมดสำหรับทุกฟังก์ชั่นในภาษาที่จำเป็น 1) คุณสูญเสียความสามารถในการสร้างกลไกโฟลว์การควบคุมของคุณเอง (โดยไม่มีมาโคร) 2) โค้ดโดยค่าเริ่มต้น3) และการใช้งานบางอย่างเช่นการเลิกทำ (การเดินทางข้ามเวลา) ต้องใช้ความระมัดระวัง (โปรแกรมเมอร์ที่จำเป็นต้องจัดเก็บสูตรสำหรับการคืนค่าเก่า!) ในขณะที่การเขียนโปรแกรมที่ใช้งานได้จริงจะซื้อทุกสิ่งเหล่านี้ให้คุณ ลืม - "ฟรี"
ฉันหวังว่านี่จะไม่ดูเหมือนความกระตือรือร้นฉันแค่ต้องการเพิ่มมุมมอง การเขียนโปรแกรมและการเขียนโปรแกรมจำเป็นโดยเฉพาะอย่างยิ่งกระบวนทัศน์ผสมในภาษาที่มีประสิทธิภาพเช่น C # 3.0 ยังคงเป็นวิธีที่มีประสิทธิภาพโดยสิ้นเชิงที่จะได้รับสิ่งที่ทำและไม่มี bullet
[1] ... ยกเว้นอาจมีการใช้หน่วยความจำด้วยความเคารพ (cf. foldl และ foldl 'ใน Haskell)
หากต้องการขยายความคิดเห็นของ Konrad:
และลำดับของการประเมินไม่ชัดเจน
ภาษาที่ใช้งานได้บางภาษามีสิ่งที่เรียกว่า Lazy Evaluation ซึ่งหมายความว่าฟังก์ชั่นจะไม่ถูกดำเนินการจนกว่าจะมีความต้องการค่า จนกว่าจะถึงเวลานั้นฟังก์ชั่นของตัวเองคือสิ่งที่ผ่านไป
ภาษาขั้นตอนคือขั้นตอนที่ 1 ขั้นตอนที่ 2 ขั้นตอนที่ 3 ... ถ้าในขั้นตอนที่ 2 คุณพูดว่าเพิ่ม 2 + 2 มันจะทำอย่างนั้น ในการประเมินผลที่ขี้เกียจคุณจะบอกว่าเพิ่ม 2 + 2 แต่ถ้าไม่เคยใช้ผลลัพธ์ก็จะไม่เพิ่มเข้าไปอีก
หากคุณมีโอกาสฉันจะแนะนำและรับสำเนา Lisp / Scheme และทำโครงการบางอย่างในนั้น ส่วนใหญ่ของความคิดที่ได้กลายเป็น bandwagons เมื่อเร็ว ๆ นี้แสดงใน Lisp ทศวรรษที่ผ่านมา: การเขียนโปรแกรมการทำงานต่อเนื่อง (เป็นปิด), การเก็บขยะแม้แต่ XML
นั่นจะเป็นวิธีที่ดีในการเริ่มต้นความคิดปัจจุบันทั้งหมดและอีกสองสามอย่างเช่นการคำนวณเชิงสัญลักษณ์
คุณควรรู้ว่าการเขียนโปรแกรมฟังก์ชั่นที่ดีสำหรับอะไรและมันไม่ดีสำหรับ มันไม่ดีสำหรับทุกสิ่ง ปัญหาบางอย่างแสดงออกได้ดีที่สุดในแง่ของผลข้างเคียงซึ่งคำถามเดียวกันจะให้คำตอบที่ต่างกันขึ้นอยู่กับเวลาที่ถาม
@Creighton:
ใน Haskell มีฟังก์ชั่นห้องสมุดชื่อproduct :
prouduct list = foldr 1 (*) list
หรือเพียงแค่:
product = foldr 1 (*)
ดังนั้น "สำนวน" แฟคทอเรียล
fac n = foldr 1 (*) [1..n]
ก็จะเป็น
fac n = product [1..n]
ขั้นตอนการโปรแกรมมิงหารลำดับของข้อความสั่งและโครงสร้างตามเงื่อนไขเป็นบล็อกแยกที่เรียกว่าโพรซีเดอร์ที่กำหนดพารามิเตอร์เหนืออาร์กิวเมนต์ที่มีค่า (ไม่สามารถใช้งานได้)
ฟังก์ชั่นการเขียนโปรแกรมเหมือนกันยกเว้นว่าฟังก์ชั่นเป็นค่าชั้นหนึ่งดังนั้นพวกเขาสามารถส่งผ่านเป็นข้อโต้แย้งไปยังฟังก์ชั่นอื่น ๆ และกลับมาเป็นผลมาจากการเรียกใช้ฟังก์ชั่น
โปรดทราบว่าการโปรแกรมเชิงฟังก์ชันนั้นเป็นลักษณะทั่วไปของการโปรแกรมเชิงโพรซีเดอร์ในการตีความนี้ อย่างไรก็ตามชนกลุ่มน้อยตีความ "ฟังก์ชั่นการเขียนโปรแกรม" เพื่อหมายถึงผลข้างเคียงที่ไม่มีความแตกต่างกันมาก แต่ไม่เกี่ยวข้องกับภาษาหลักทั้งหมดยกเว้น Haskell
การทำความเข้าใจความแตกต่างหนึ่งต้องเข้าใจว่า "เจ้าพ่อ" กระบวนทัศน์ของทั้งสองขั้นตอนการเขียนโปรแกรมและการทำงานเป็นคำสั่งโปรแกรม
การเขียนโปรแกรมตามขั้นตอนพื้นฐานเป็นเพียงวิธีการจัดโครงสร้างโปรแกรมที่จำเป็นซึ่งวิธีการหลักของนามธรรมเป็น "ขั้นตอน" (หรือ "ฟังก์ชั่น" ในภาษาการเขียนโปรแกรมบางภาษา) แม้แต่การเขียนโปรแกรมเชิงวัตถุก็เป็นอีกวิธีหนึ่งในการสร้างโปรแกรมที่จำเป็นซึ่งสถานะถูกห่อหุ้มในวัตถุกลายเป็นวัตถุด้วย "สถานะปัจจุบัน" รวมทั้งวัตถุนี้มีชุดของฟังก์ชั่นวิธีการและสิ่งอื่น ๆ ที่ช่วยให้คุณ โปรแกรมเมอร์จัดการหรือปรับปรุงสถานะ
ตอนนี้เกี่ยวกับการเขียนโปรแกรมฟังก์ชั่นส่วนสำคัญในวิธีการของมันคือมันระบุค่าที่จะใช้และวิธีการถ่ายโอนค่าเหล่านี้ (ดังนั้นจึงไม่มีสถานะและไม่มีข้อมูลที่ไม่แน่นอนในขณะที่ใช้ฟังก์ชั่นเป็นค่าระดับเฟิร์สคลาสและส่งผ่านเป็นพารามิเตอร์ไปยังฟังก์ชั่นอื่น ๆ )
PS: การทำความเข้าใจกับทุกกระบวนทัศน์การเขียนโปรแกรมที่ใช้ควรชี้แจงความแตกต่างระหว่างพวกเขาทั้งหมด
PSS: ในตอนท้ายของวันกระบวนทัศน์การเขียนโปรแกรมเป็นเพียงวิธีการที่แตกต่างกันในการแก้ปัญหา
PSS: คำตอบ quora นี้มีคำอธิบายที่ดี
ไม่มีคำตอบที่นี่แสดงการเขียนโปรแกรมฟังก์ชั่นสำนวน คำตอบแบบแฟคทอเรียลแบบเรียกซ้ำนั้นยอดเยี่ยมสำหรับการแสดงแบบเรียกซ้ำใน FP แต่โค้ดส่วนใหญ่ไม่ได้เรียกซ้ำดังนั้นฉันจึงไม่คิดว่าคำตอบนั้นเป็นตัวแทนอย่างสมบูรณ์
สมมติว่าคุณมีอาร์เรย์ของสตริงและแต่ละสตริงแทนจำนวนเต็มเช่น "5" หรือ "-200" คุณต้องการตรวจสอบอาร์เรย์อินพุตของสตริงกับกรณีทดสอบภายในของคุณ (ใช้การเปรียบเทียบจำนวนเต็ม) โซลูชันทั้งสองแสดงอยู่ด้านล่าง
arr_equal(a : [Int], b : [Str]) -> Bool {
if(a.len != b.len) {
return false;
}
bool ret = true;
for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
int a_int = a[i];
int b_int = parseInt(b[i]);
ret &= a_int == b_int;
}
return ret;
}
eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization
arr_equal(a : [Int], b : [Str]) -> Bool =
zip(a, b.map(toInt)) # Combines into [Int, Int]
.map(eq)
.reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value
ในขณะที่ภาษาที่ใช้งานได้จริงเป็นภาษาที่ใช้ในการวิจัย (ในขณะที่โลกแห่งความเป็นจริงชอบผลข้างเคียงฟรี) ภาษาขั้นตอนในโลกแห่งความเป็นจริงจะใช้ไวยากรณ์การทำงานที่ง่ายกว่ามากเมื่อเหมาะสม
นี้มักจะนำมาใช้กับห้องสมุดภายนอกเช่นLodashหรือที่มีอยู่ในตัวกับภาษาใหม่เช่นสนิม ยกของหนักของการเขียนโปรแกรมการทำงานจะทำด้วยฟังก์ชั่น / แนวคิดเช่นmap
, filter
, reduce
, currying
,partial
, ครั้งที่สามของการที่คุณสามารถมองขึ้นสำหรับการทำความเข้าใจต่อไป
เพื่อที่จะใช้ในไวลด์คอมไพเลอร์โดยปกติจะต้องหาวิธีแปลงเวอร์ชันการทำงานเป็นเวอร์ชันโพรซีเดอร์ภายในเนื่องจากการเรียกใช้ฟังก์ชันสูงเกินไป กรณีแบบเรียกซ้ำเช่นแฟคทอเรียลที่แสดงจะใช้ลูกเล่นเช่นการโทรหางเพื่อลบการใช้หน่วยความจำ O (n) ความจริงที่ว่าไม่มีผลข้างเคียงช่วยให้คอมไพเลอร์ทำงานเพื่อใช้การปรับให้&& ret
เหมาะสมแม้ว่า.reduce
จะดำเนินการเสร็จสิ้นแล้วก็ตาม การใช้ Lodash ใน JS เห็นได้ชัดว่าไม่อนุญาตให้มีการปรับให้เหมาะสมดังนั้นจึงเป็นที่นิยมสำหรับประสิทธิภาพ (ซึ่งมักไม่เกี่ยวข้องกับการพัฒนาเว็บ) ภาษาเช่น Rust จะปรับให้เหมาะสมภายใน (และมีฟังก์ชั่นเช่นtry_fold
เพื่อช่วย&& ret
เพิ่มประสิทธิภาพ)