เหตุใดภาษาการเขียนโปรแกรมที่ใช้งานได้บางภาษาจึงใช้พื้นที่สำหรับแอปพลิเคชันฟังก์ชั่น


34

เมื่อมองไปที่บางภาษาสำหรับการเขียนโปรแกรมการทำงานฉันมักจะสงสัยว่าทำไมบางภาษา fp จึงใช้อักขระช่องว่างอย่างน้อยหนึ่งตัวสำหรับแอปพลิเคชันฟังก์ชัน (และคำจำกัดความ) ในขณะที่ภาษาเชิงวัตถุ / เชิงวัตถุส่วนใหญ่ เป็นวิธีทางคณิตศาสตร์มากขึ้น ฉันคิดว่าสไตล์หลังมีความชัดเจนและอ่านง่ายกว่าที่ไม่มีการเลียนแบบ

ดังนั้นถ้าเรามีฟังก์ชั่นf (x) = x²มีสองทางเลือกในการเรียกมันว่า:

  • FP: f x

    ตัวอย่าง:

    • ML, Ocaml, F #
    • Haskell
    • LISP โครงการ (อย่างใด)
  • Non-FP: f(x)

    ตัวอย่าง:

    • เกือบทุกภาษาที่จำเป็น (ฉันรู้ว่าเห็นความเห็น / คำตอบ)
    • Erlang
    • Scala (อนุญาตให้ "สัญกรณ์ตัวดำเนินการ" สำหรับอาร์กิวเมนต์เดี่ยว)

อะไรคือสาเหตุของการ "ลาออก" วงเล็บคืออะไร?


9
ที่จะทำให้มันมากขึ้นทำให้เกิดความสับสน, ERM, ฉันหมายรวบรัด
Den

11
@Den ถ้าคุณเรียนรู้ Haskell, ไวยากรณ์จะเป็นหนึ่งในสิ่งที่สับสนน้อยที่สุด: p
Simon Bergot

5
คุณตระหนักถึงความประชดของการบอกว่าการใช้วงเล็บจะเป็นคณิตศาสตร์มากกว่าและจากนั้นใช้ฟังก์ชันการยกกำลังเป็นตัวอย่าง?
Jörg W Mittag

12
@Den คุณกำลังทำร้ายตัวเองโดยการปฏิเสธภาษาเพราะไวยากรณ์ของมัน แน่นอนว่าคุณไม่มีปัญหาในการเรียนรู้ xml หรือ sql (ไม่ใช่ภาษาวัตถุประสงค์ทั่วไป แต่พวกเขากำหนดไวยากรณ์ของพวกเขาเอง)
Simon Bergot

7
ฉันต้องการชี้ให้เห็นว่าเชลล์สคริปต์ (เช่น bash) โดยทั่วไปจะไม่ใช้พารามิเตอร์เพื่อเรียกคำสั่ง การเรียงลำดับของ PowerShell นั้นเลวร้ายที่สุดของโลกทั้งสองในฟังก์ชันนั้นถูกประกาศด้วยวงเล็บและเรียกโดยไม่มีพวกมัน
Kris Harper

คำตอบ:


59

ซึ่งดูเหมือนจะเป็นวิธีทางคณิตศาสตร์มากขึ้น

ภาษาที่ใช้งานได้แรงบันดาลใจจากแลมบ์ดาแคลคูลัส ในฟิลด์นี้จะไม่ใช้วงเล็บสำหรับแอปพลิเคชันฟังก์ชัน

ฉันคิดว่าสไตล์หลังมีความชัดเจนและอ่านง่ายกว่าที่ไม่มีการเลียนแบบ

การอ่านอยู่ในสายตาของคนดู คุณไม่คุ้นเคยกับการอ่าน มันเหมือนกับตัวดำเนินการทางคณิตศาสตร์ หากคุณเข้าใจความสัมพันธ์คุณต้องใช้เพียงไม่กี่ parens ในการอธิบายโครงสร้างการแสดงออกของคุณ บ่อยครั้งที่คุณไม่ต้องการพวกเขา

การแกงยังเป็นเหตุผลที่ดีที่จะใช้การประชุมนี้ ใน Haskell คุณสามารถกำหนดสิ่งต่อไปนี้:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

ด้วย parens คุณสามารถใช้สองการประชุม: f(5, 6)(ไม่ใช่ curried) หรือf(5)(6)(curried) ไวยากรณ์ haskell ช่วยให้คุ้นเคยกับแนวคิดการปิดกั้น คุณยังสามารถใช้เวอร์ชันที่ไม่ใช่ curated แต่มันเจ็บปวดกว่าที่จะใช้กับ combinators

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

โปรดสังเกตว่ารุ่นที่สองบังคับให้คุณลงทะเบียน x เป็นตัวแปรอย่างไรและ subexpression เป็นฟังก์ชันที่ใช้จำนวนเต็มและเพิ่ม 5 ลงไป เวอร์ชั่น curried นั้นเบากว่ามาก แต่ก็ยังถือว่ามีผู้อ่านให้อ่านมากกว่านี้

โปรแกรม Haskell ใช้งานแอปพลิเคชั่นบางส่วนและคอมบิเนเตอร์อย่างครอบคลุมเพื่อกำหนดและเขียน abstractions ดังนั้นนี่ไม่ใช่ตัวอย่างของเล่น ฟังก์ชั่นอินเทอร์เฟซที่ดีจะเป็นที่ที่ลำดับของพารามิเตอร์ให้การใช้งาน curried ที่เป็นมิตร

จุดอื่น: f()ฟังก์ชั่นโดยไม่มีพารามิเตอร์ควรจะเรียกว่ามี ใน haskell เนื่องจากคุณจัดการค่าที่ประเมินค่าไม่ได้ของ lazy คุณเพียงแค่เขียนfและคิดว่ามันเป็นค่าที่จะต้องทำการคำนวณบางอย่างเมื่อจำเป็น เนื่องจากการประเมินผลจะไม่มีผลข้างเคียงใด ๆ จึงไม่มีเหตุผลที่จะมีสัญกรณ์ที่แตกต่างกันสำหรับฟังก์ชั่นไร้พารามิเตอร์และค่าที่ส่งคืน

นอกจากนี้ยังมีข้อกำหนดอื่น ๆ สำหรับแอปพลิเคชันฟังก์ชัน:

  • Lisp: (fx) - ส่วนนำหน้าด้วยวงเล็บภายนอก
  • ไปมา: xf - postfix

7
ผู้เยาว์ nitpick: คำว่า "currying" และ "curried" ไม่ใช่ "currification" และ "currified"
Doval

"ไม่ใช่ curried" ฟังก์ชันที่ไม่ใช่ curried ใน Haskell คืออะไร
เวน

3
@ user1737909 ฟังก์ชันที่รับ tuple เป็นอาร์กิวเมนต์
Doval

1
@Doval จริงๆแล้วมันควรจะเป็น "schönfinkeling" และ "schönfinkeled" แต่โอ้ดีประวัติศาสตร์กำหนดผู้ชนะย้อนหลังเสมอ
Profpatsch

ความคิดเกี่ยวกับไวยากรณ์วงเล็บ currying บางอย่างเช่นf(a, ,b)หรือadd(a, )หรือadd(, b)จะดูเหมือนมีความยืดหยุ่นมากขึ้นโดยเริ่มต้น
phresnel

25

แนวคิดพื้นฐานคือการทำให้การทำงานที่สำคัญที่สุด (ฟังก์ชั่นแอปพลิเคชั่น) ง่ายที่สุดในการอ่านและเขียนได้ง่ายที่สุด พื้นที่อ่านไม่ได้อ่านง่ายและพิมพ์ง่ายมาก

โปรดทราบว่านี่ไม่ใช่เฉพาะกับภาษาที่ใช้งานได้เช่นในSmalltalkหนึ่งในภาษา OO และภาษาแรกที่ได้รับแรงบันดาลใจจากมัน ( Self , Newspeak , Objective-C) แต่ใน Io และภาษาที่ได้รับแรงบันดาลใจจากมัน (Ioke, Seph) ช่องว่างใช้สำหรับการเรียกใช้เมธอด

สมอลล์ทอล์คสไตล์:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(ในกรณีหลังชื่อของวิธีการคือreplace:with:)

ไอโอสไตล์:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala อนุญาตช่องว่างสำหรับการเรียกใช้เมธอด:

foo bar(baz)

และทิ้งวงเล็บไว้หากมีเพียงอาร์กิวเมนต์เดียวเท่านั้น:

foo bar baz

Ruby ยังช่วยให้คุณสามารถออกจากวงเล็บ:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

ใช้วงเล็บซึ่งดูเหมือนจะเป็นวิธีการทางคณิตศาสตร์มากขึ้น

ไม่จริง:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

สัญกรณ์คณิตศาสตร์ได้พัฒนามาเป็นเวลานานในลักษณะที่ไม่สอดคล้องกันอย่างน่ากลัว

ถ้าในทุกภาษาฟังก์ชั่นการใช้แรงบันดาลใจจาก from-แคลคูลัสที่เขียนฟังก์ชั่นการใช้งานโดยใช้ช่องว่าง


3
นอกจากนี้ยังมีข้อโต้แย้งเกี่ยวกับการประหยัดในการแก้ไข โดยเฉพาะอย่างยิ่งภาษาที่ใช้งานได้มักจะสนับสนุนองค์ประกอบโดยตรง การใส่วงเล็บไว้รอบ ๆ ฟังก์ชั่นแทนการโต้แย้งก็สมเหตุสมผล คุณต้องทำ "ครั้งเดียว": (e. f. g) x ซึ่งตรงข้ามกับ e (f (g (x))) อย่างน้อย Haskell ก็จะไม่หยุดใครจากการทำ f (x) แทน f x มันไม่ใช่สำนวน
ผู้หญิง

18

วงเล็บสำหรับการใช้งานฟังก์ชั่นเป็นหนึ่งในอานออยเลอร์จำนวนมากที่ทิ้งเราไว้ เช่นเดียวกับคณิตศาสตร์อื่น ๆ ที่ต้องการการประชุมเมื่อมีหลายวิธีที่จะทำอะไรบางอย่าง หากการศึกษาทางคณิตศาสตร์ของคุณขยายไปถึงวิชาที่ไม่ใช่วิชาคณิตศาสตร์ในมหาวิทยาลัยคุณอาจไม่คุ้นเคยกับหลายสาขาที่การใช้งานฟังก์ชั่นเกิดขึ้นอย่างมีความสุขโดยไม่ต้องมีวงเล็บไร้สาระใด ๆ เหล่านี้ (เช่นเรขาคณิตเชิงอนุพันธ์ และคุณยังไม่ได้พูดถึงฟังก์ชั่นที่ใช้ infix (เกือบทุกภาษา), "outfix" เช่นการใช้บรรทัดฐานหรือไดอะแกรม (Befunge, Topology เกี่ยวกับพีชคณิต)

ดังนั้นในคำตอบฉันจะบอกว่ามันเป็นเพราะสัดส่วนที่สูงขึ้นของโปรแกรมเมอร์และนักออกแบบภาษาที่มีการศึกษาทางคณิตศาสตร์อย่างกว้างขวาง Von Neumann กล่าวว่า "ชายหนุ่มในวิชาคณิตศาสตร์คุณไม่เข้าใจสิ่งต่าง ๆ คุณเพิ่งชินกับมัน" แน่นอนว่านี่เป็นความจริงของสัญกรณ์


7
อย่าเพิ่งเริ่มต้นf(x)กับsin xvs. vs. |x|vs. x!vs. x + yvs. xyvs. ½vs. summation เทียบกับ integral กับ…
Jörg W Mittag

2
+1 สำหรับคำพูดของ von Neuman +1 สำหรับคำตอบที่เหลือ แต่ฉันไม่สามารถให้ +2 :(
pvorb

2
ฉันจับนัยของอภิสิทธิ์ที่นี่; ผมโต้แย้งความเป็นกลางของ"นักออกแบบภาษาการเขียนโปรแกรมการทำงานได้รับการศึกษาที่ดีกว่า"
CaptainCodeman

7
@CaptainCodeman เขากล่าวว่ามีการศึกษาเพิ่มเติมในวิชาคณิตศาสตร์คำสั่งที่ฉันเห็นด้วย อย่างน้อยเมื่อพูดถึง Haskell คุณสามารถสังเกตได้ว่าแนวคิดทั้งสองนั้นเป็นคณิตศาสตร์มากกว่าในธรรมชาติและชุมชนก็มีความโน้มเอียงทางคณิตศาสตร์มากกว่าเช่นกัน พวกมันไม่ฉลาดกว่ามีการศึกษาทางคณิตศาสตร์ดีกว่า
Paul

5
@CaptainCodeman ไม่เนื่องจากเขาไม่เคยบอกเป็นนัยว่าพวกเขาได้รับการศึกษาโดยทั่วไปมากกว่ามีการศึกษาทางคณิตศาสตร์มากขึ้น นั่นคือสิ่งที่เขาพูดว่า: "การศึกษาคณิตศาสตร์อย่างกว้างขวาง" :)
Paul

8

แม้ว่าคำตอบของไซมอนจะมีความจริงมากมาย แต่ฉันคิดว่ามันมีเหตุผลที่เป็นประโยชน์มากกว่า ลักษณะของการเขียนโปรแกรมฟังก์ชั่นมีแนวโน้มที่จะสร้างวงเล็บมากขึ้นกว่าการเขียนโปรแกรมที่จำเป็นเนื่องจากการผูกมัดฟังก์ชั่นและองค์ประกอบ รูปแบบของการผูกมัดและองค์ประกอบเหล่านั้นมักจะเกิดขึ้นเพื่อให้สามารถแสดงได้อย่างชัดเจนโดยไม่ต้องวงเล็บ

บรรทัดล่างคือวงเล็บทั้งหมดที่ได้รับน่ารำคาญในการอ่านและติดตาม อาจเป็นเหตุผลอันดับหนึ่งที่ LISP ไม่ได้รับความนิยมมากขึ้น ดังนั้นหากคุณสามารถรับพลังของการเขียนโปรแกรมฟังก์ชั่นโดยไม่ต้องรำคาญกับวงเล็บส่วนเกินฉันคิดว่านักออกแบบภาษาจะมีแนวโน้มที่จะเป็นเช่นนั้น ท้ายที่สุดนักออกแบบภาษาก็เป็นผู้ใช้ภาษาเช่นกัน

แม้แต่ Scala ยังอนุญาตให้โปรแกรมเมอร์ละเว้นวงเล็บในบางสถานการณ์ซึ่งเกิดขึ้นบ่อยครั้งเมื่อทำการเขียนโปรแกรมในรูปแบบการใช้งานดังนั้นคุณจะได้รับสิ่งที่ดีที่สุดจากทั้งสองโลก ตัวอย่างเช่น:

val message = line split "," map (_.toByte)

parens ในตอนท้ายมีความจำเป็นสำหรับการเชื่อมโยงและส่วนอื่น ๆ จะถูกปล่อยออก (เช่นเดียวกับจุด) การเขียนด้วยวิธีนี้จะเน้นธรรมชาติของการปฏิบัติการที่คุณกำลังแสดง มันอาจไม่คุ้นเคยกับโปรแกรมเมอร์ที่จำเป็น แต่สำหรับโปรแกรมเมอร์ที่ใช้งานได้มันให้ความรู้สึกเป็นธรรมชาติและไหลลื่นในการเขียนด้วยวิธีนี้โดยไม่ต้องหยุดและแทรกไวยากรณ์ที่เพิ่มความหมายไม่มีโปรแกรม แต่มันทำให้ผู้แปลมีความสุข .


2
"บรรทัดล่างคือวงเล็บทั้งหมดนั้นสร้างความรำคาญในการอ่านและติดตามมันอาจเป็นเหตุผลอันดับหนึ่งที่ LISP ไม่เป็นที่นิยมมากขึ้น": ฉันไม่คิดว่าวงเล็บเป็นเหตุผลที่ Lisp จะไม่เป็นที่นิยม: ฉันไม่คิดว่า พวกเขาอ่านยากโดยเฉพาะและฉันพบว่าภาษาอื่นมีไวยากรณ์ที่น่าอึดอัดใจกว่ามาก
Giorgio

2
ไวยากรณ์ LISP ส่วนใหญ่ประกอบด้วยวงเล็บที่น่ารำคาญซึ่งมีระดับมุมฉากสูงเป็นพิเศษ นั่นหมายถึงว่ามันมีคุณสมบัติการไถ่อื่น ๆ ที่ทำให้มันยังใช้งานได้อยู่ไม่ใช่ว่า parens ไม่น่ารำคาญ ฉันไม่เข้าใจว่าทุกคนรู้สึกอย่างนั้น แต่โปรแกรมเมอร์ส่วนใหญ่ที่ฉันเคยพบทำ
Karl Bielefeldt

"Lost In Stupid วงเล็บ"เป็นตัวย่อ LISP ที่ฉันโปรดปราน คอมพิวเตอร์ยังสามารถใช้วิธีที่มีมนุษยธรรมเพื่อระบุรหัสบล็อกและวางความซ้ำซ้อนในขณะที่ภาษาเช่นWisp
Cees Timmerman

4

สถานที่ทั้งสองผิด

  • ภาษาที่ใช้งานได้เหล่านี้ไม่ได้ใช้พื้นที่สำหรับแอปพลิเคชันฟังก์ชัน สิ่งที่พวกเขาทำก็แค่แยกการแสดงออกที่เกิดขึ้นหลังจากฟังก์ชั่นเป็นอาร์กิวเมนต์

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    แน่นอนว่าถ้าคุณใช้ทั้งที่ว่างและที่ว่างก็ไม่สามารถใช้งานได้เพราะการแสดงออกไม่ถูกต้อง

    GHCi> f3

    <‌ แบบโต้ตอบ>: 7: 1:
        ไม่อยู่ในขอบเขต: 'f3'
        บางทีคุณอาจหมายถึง 'f' (บรรทัด 2)

    แต่ถ้าภาษาเหล่านี้ถูก จำกัด ไว้ที่ชื่อตัวแปร 1 ตัวอักษรก็จะใช้ได้เช่นกัน

  • โดยทั่วไปแล้วหลักการคณิตศาสตร์ก็ไม่จำเป็นต้องใช้วงเล็บในการใช้ฟังก์ชั่น นักคณิตศาสตร์มักจะเขียน sin x - สำหรับฟังก์ชั่นเชิงเส้น (โดยทั่วไปเรียกว่าตัวดำเนินการเชิงเส้น) นอกจากนี้ยังเป็นเรื่องปกติมากในการเขียนอาร์กิวเมนต์โดยไม่มี parens บางทีอาจเป็นเพราะ (เหมือนกับฟังก์ชั่นใด ๆ ในการเขียนโปรแกรมเชิงฟังก์ชัน) ข้อโต้แย้ง

ดังนั้นคำถามของคุณถึง: ทำไมบางภาษาต้องการวงเล็บสำหรับการใช้ฟังก์ชั่น? เห็นได้ชัดว่าบางคนเช่นคุณพิจารณาเรื่องนี้อ่านง่ายขึ้น ฉันไม่เห็นด้วยอย่างยิ่งกับเรื่องนี้: parens คู่หนึ่งเป็นสิ่งที่ดี แต่ทันทีที่คุณมีคนสองคนซ้อนกันมันจะเริ่มสับสน รหัสเสียงกระเพื่อมแสดงให้เห็นถึงสิ่งที่ดีที่สุดนี้และภาษาที่ใช้งานได้ทั้งหมดจะดูยุ่งเหยิงหากคุณต้องการ parens ทุกที่ สิ่งนี้ไม่ได้เกิดขึ้นมากในภาษาที่จำเป็น แต่ส่วนใหญ่เป็นเพราะภาษาเหล่านี้ไม่ได้มีความหมายมากพอที่จะเขียนหนึ่ง - liners รัดกุมชัดเจนในตอนแรก


มีฉันคำถามที่ไม่สมบูรณ์ :) ดังนั้นข้อสรุปที่จะวาดคือ: "วงเล็บไม่ขยาย"
pvorb

2
หรือเป็นภาษาที่ไม่สามารถใช้งานได้ซึ่งมีมติเป็นเอกฉันท์ในการกำหนดวงเล็บ สำหรับตัวอย่างเล็ก ๆ น้อย ๆ Smalltalk มีobject method: argsวัตถุประสงค์ C มี[object method: args]และทับทิมและ Perl ทั้งสองอนุญาตให้ละเว้นวงเล็บในการทำงานและการเรียกวิธีการเพียงต้องการให้พวกเขาแก้ไขความกำกวม
ฮอบส์

1

ชี้แจงประวัติศาสตร์ขนาดเล็ก: สัญกรณ์ต้นฉบับในคณิตศาสตร์ได้ โดยไม่ต้องวงเล็บ !

สัญกรณ์fxสำหรับฟังก์ชันตามอำเภอใจของxได้รับการแนะนำโดย Johan Bernoulli ประมาณ 1,700 ไม่นานหลังจาก Leibniz เริ่มพูดถึงฟังก์ชั่น (ที่จริงแล้ว Bernoulli เขียนϕ x ) แต่ก่อนหน้านั้นมีคนจำนวนมากที่ใช้สัญลักษณ์เช่น sin xหรือlx (ลอการิทึมของx ) สำหรับฟังก์ชันเฉพาะของxโดยไม่มีวงเล็บ ฉันสงสัยว่าเหตุผลที่คนจำนวนมากในวันนี้ยังคงเขียนบาปxแทนบาป(x)

ออยเลอร์ผู้เป็นนักเรียนของ Bernoulli เป็นลูกบุญธรรมของ Bernoulli φ xและเปลี่ยนกรีกเป็นตัวอักษรละตินเขียนFX หลังจากนั้นเขาก็สังเกตเห็นว่ามันอาจจะเป็นเรื่องง่ายที่จะสับสนสำหรับ "ปริมาณ" และเชื่อว่าFXเป็นผลิตภัณฑ์เพื่อให้เขาเปิดตัวเขียนf: x ดังนั้นสัญกรณ์f (x)จึงไม่ได้เกิดจากออยเลอร์ แน่นอนออยเลอร์จำเป็นต้องวงเล็บด้วยเหตุผลเดียวกันเรายังคงต้องวงเล็บในภาษาเขียนโปรแกรมการทำงานที่จะแยกแยะความแตกต่างเช่น(FX) + Yจากf (x + y) ฉันสงสัยว่าคนหลังจากออยเลอร์ใช้วงเล็บเป็นค่าเริ่มต้นเพราะพวกเขาเห็นออยเลอร์เขียนสำนวนรวมเช่นf (ax + b). เขา seldomly เขียนเพียงFX

อ่านเพิ่มเติมเกี่ยวกับเรื่องนี้: ออยเลอร์เคยเขียน f (x) ด้วยวงเล็บหรือไม่? .

ฉันไม่รู้ว่านักออกแบบของภาษาโปรแกรมที่ใช้งานได้หรือนักประดิษฐ์ของแคลคูลัสแลมบ์ดารู้ถึงรากเหง้าทางประวัติศาสตร์ของสัญกรณ์ของพวกเขาหรือไม่ โดยส่วนตัวแล้วฉันพบว่าสัญกรณ์ที่ไม่มีวงเล็บเป็นธรรมชาติมากกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.