ชนิดของข้อมูลเกี่ยวกับพีชคณิตแก้ปัญหาอะไร?


18

คำเตือนอย่างเป็นธรรมฉันยังใหม่กับการเขียนโปรแกรมที่ใช้งานได้ดังนั้นฉันอาจมีข้อสันนิษฐานที่ไม่ดีมากมาย

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

public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
    public T Value { get; set; }
}

var result = GetSomeValue();
if(result is None)
{
}
else
{
}

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


6
หน้าที่การเขียนโปรแกรมเป็นกระบวนทัศน์ที่แตกต่างจากการเขียนโปรแกรมเชิงวัตถุ
Basile Starynkevitch

@BasileStarynkevitch ฉันรู้ แต่มีภาษาเช่น F # ที่ทั้งสองเล็กน้อย คำถามนั้นไม่ได้เกี่ยวกับภาษาที่ใช้งานได้ แต่เกี่ยวกับประเภทของข้อมูลเชิงพีชคณิตเกี่ยวกับปัญหา
ConditionRacer

7
เกิดอะไรขึ้นเมื่อฉันกำหนดclass ThirdOption : Option{}และให้คุณnew ThirdOption()ที่คุณคาดว่าจะได้SomeหรือNone?
amon

1
@ ภาษามอญที่มีประเภทผลรวมมักมีวิธีไม่อนุญาต ตัวอย่างเช่น Haskell ให้คำจำกัดความdata Maybe a = Just a | Nothing(เทียบเท่ากับdata Option a = Some a | Noneตัวอย่างของคุณ): คุณไม่สามารถเพิ่มตัวพิมพ์เล็ก - ใหญ่ได้ แม้ว่าคุณจะสามารถเลียนแบบประเภทผลรวมใน C # ในแบบที่คุณแสดง แต่ก็ไม่ได้สวยที่สุด
Martijn

1
ฉันคิดว่ามันน้อยลง "สิ่งที่ ADTs ทำคือการแก้ปัญหา" และอื่น ๆ เช่น "ADTs นั้นเป็นวิธีที่แตกต่างกันในการเข้าถึงสิ่งต่างๆ"
คณิตศาสตร์ออร์คิด

คำตอบ:


44

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

พีชคณิตชนิดข้อมูลที่เป็นคู่เพื่อที่พวกเขาจะปิด ทุกกรณีของข้อมูลที่มีการระบุไว้ในที่เดียวและการดำเนินงานไม่เพียง แต่สามารถแสดงรายชื่อสายพันธุ์อย่างละเอียดถี่ถ้วนพวกเขาจะได้รับการสนับสนุนให้ทำเช่นนั้น ดังนั้นการเขียนฟังก์ชั่นใหม่ที่ใช้งานกับชนิดข้อมูลพีชคณิตนั้นไม่สำคัญ: เพียงแค่เขียนฟังก์ชั่นแช่ง ในทางกลับกันการเพิ่มเคสใหม่นั้นซับซ้อนเพราะคุณต้องผ่านฐานรหัสทั้งหมดและขยายทุกmatchกรณี คล้ายกับสถานการณ์ที่มีอินเทอร์เฟซในไลบรารีมาตรฐานของสนิมการเพิ่มชุดตัวเลือกใหม่คือการเปลี่ยนแปลงที่ไม่คาดคิด (สำหรับประเภทสาธารณะ)

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


คุณได้รับข้อผิดพลาดของคอมไพเลอร์หรือไม่หากคุณไม่สามารถอัพเดตนัดการแข่งขันได้หรือเฉพาะตอนรันไทม์ที่คุณพบ
เอียน

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

นอกจากนี้การตรวจสอบแบบคงที่ว่ารูปแบบหมดจดโดยทั่วไปมีราคาแพง มันแก้ปัญหา SAT ได้ดีที่สุดและแย่ที่สุดภาษาที่อนุญาตให้ใช้เพรดิเคตตามอำเภอใจซึ่งทำให้ไม่สามารถตัดสินใจได้
usr

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

ลองนึกภาพคุณกำลังจับคู่กับ N booleans จากนั้นคุณเพิ่มคำที่ตรงกันเช่น (a, b, _, d, ... ) เคสทั้งหมดเป็นแบบนั้น! clause1 &&! clause2 && .... ดูเหมือนว่า SAT ถึงฉัน
usr

12

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

นั่นอาจเป็นการทำให้เข้าใจง่าย แต่ก็ใช่

มีอะไรอีกบ้างที่ขาดหายไป?

ให้ชัดเจนเกี่ยวกับประเภทข้อมูลพีชคณิต (สรุปลิงก์ที่ดีนี้จาก Learn you as Haskell):

  • ประเภทผลรวมที่ระบุว่า "ค่านี้อาจเป็น A หรือ B"
  • ประเภทผลิตภัณฑ์ที่ระบุว่า "ค่านี้มีทั้ง A และ A "

ตัวอย่างของคุณใช้ได้กับตัวอย่างแรกเท่านั้น

สิ่งที่คุณอาจขาดหายไปก็คือการให้การดำเนินการพื้นฐานสองอย่างนี้ภาษาที่ใช้งานได้ช่วยให้คุณสร้างทุกอย่างได้ C # มีโครงสร้าง, คลาส, enums, ข้อมูลทั่วไปด้านบนของสิ่งเหล่านั้นและกองกฎเพื่อควบคุมพฤติกรรมของสิ่งเหล่านี้

เมื่อรวมกับไวยากรณ์บางอย่างที่จะช่วยให้ภาษาที่ใช้งานได้สามารถย่อยสลายการดำเนินการไปยังสองเส้นทางนี้ได้ซึ่งเป็นวิธีการที่สะอาดเรียบง่ายและสง่างามสำหรับประเภท

ชนิดของข้อมูลเกี่ยวกับพีชคณิตแก้ปัญหาอะไร?

พวกเขาแก้ปัญหาเดียวกับระบบประเภทอื่น ๆ : "ค่าใดที่ถูกต้องตามกฎหมายที่จะใช้ที่นี่" - พวกเขาใช้แนวทางที่ต่างออกไป


4
ประโยคสุดท้ายของคุณก็เหมือนกับบอกใครบางคนว่า "เรือแก้ปัญหาเช่นเดียวกับเครื่องบิน: การขนส่ง - พวกเขาใช้วิธีที่แตกต่าง" มันเป็นแถลงการณ์ที่ถูกต้องสมบูรณ์และเป็นข้อความที่ไร้ประโยชน์
Mehrdad

@ mehrdad - ฉันคิดว่ามันเกินเลยไปนิดหน่อย
Telastyn

1
ไม่ต้องสงสัยเลยว่าคุณเข้าใจประเภทข้อมูลเกี่ยวกับพีชคณิตได้ดี แต่รายการสัญลักษณ์แสดงหัวข้อย่อยของคุณ ("ผลรวมประเภทคือ ... " และ "ประเภทผลิตภัณฑ์คือ ... ") ดูเหมือนเป็นคำอธิบายของสหภาพและประเภทจุดตัดซึ่งไม่ใช่ ค่อนข้างเหมือนกับผลรวมและประเภทผลิตภัณฑ์
pyon

6

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

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

val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")

สังเกตวิธีmapและfilterให้คุณทำการเชื่อมโยงตัวเลือกโดยไม่ต้องตรวจสอบในแต่ละจุดว่าคุณมีNoneหรือไม่ จากนั้นในตอนท้ายคุณใช้getOrElseเพื่อระบุค่าเริ่มต้น คุณไม่มีทางทำอะไรบางอย่าง "ขั้นต้น" เหมือนการตรวจสอบประเภท การตรวจสอบประเภทที่หลีกเลี่ยงไม่ได้จะดำเนินการภายในไลบรารี Haskell มีชุดฟังก์ชั่นแบบอะนาล็อกของตัวเองไม่ต้องพูดถึงชุดฟังก์ชั่นขนาดใหญ่ที่จะทำงานกับ monad หรือ functor ใด ๆ

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

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