เหตุใดภาษาการเขียนโปรแกรมส่วนใหญ่จึงสนับสนุนการส่งคืนค่าเดียวจากฟังก์ชั่นเท่านั้น [ปิด]


118

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

ในภาษาส่วนใหญ่มีความเป็นไปได้ที่จะ "หลีกเลี่ยง" ข้อ จำกัด นั้นเช่นโดยการใช้พารามิเตอร์, พอยน์เตอร์ที่ส่งคืนหรือโดยการกำหนด / ส่งกลับ structs / คลาส แต่ดูเหมือนว่าแปลกที่ภาษาการเขียนโปรแกรมไม่ได้ถูกออกแบบมาเพื่อรองรับค่าตอบแทนที่หลากหลายในทาง "เป็นธรรมชาติ"

มีคำอธิบายสำหรับเรื่องนี้?


40
ทำให้คุณสามารถกลับอาร์เรย์ ...
นาธานทุ่งหญ้า

6
ดังนั้นทำไมไม่อนุญาตให้โต้แย้งเพียงข้อเดียว? ฉันสงสัยว่ามันเชื่อมโยงกับคำพูด นำความคิดบางอย่าง smoosh 'em เป็นสิ่งหนึ่งที่คุณกลับไปที่ใครก็ตามที่โชคดี / โชคร้ายพอที่จะฟัง การกลับมาเป็นเหมือนความเห็น "นี่" คือสิ่งที่คุณทำกับ "เหล่านี้"
Erik Reppen

30
ผมเชื่อว่าวิธีการหลามค่อนข้างง่ายและสง่างาม: เมื่อคุณมีการคืนค่าหลายค่าก็กลับ tuple A: def f(): return (1,2,3)แล้วคุณสามารถใช้ tuple-เอาออกไป "แยก" tuple a,b,c = f() #a=1,b=2,c=3นี้: ไม่จำเป็นต้องสร้างอาร์เรย์และแยกองค์ประกอบด้วยตนเองไม่จำเป็นต้องกำหนดคลาสใหม่
Bakuriu

7
ฉันสามารถบอกคุณได้ว่า Matlab มีค่าที่ส่งคืนจำนวนตัวแปร จำนวนของการขัดแย้งที่ส่งออกจะถูกกำหนดโดยลายเซ็นโทร (เช่น[a, b] = f()กับ[a, b, c] = f()) และได้รับภายในโดยf nargoutฉันไม่ใช่แฟนตัวยงของ Matlab แต่จริงๆแล้วมันมีประโยชน์หลายครั้ง
gerrit

5
ฉันคิดว่าถ้าภาษาโปรแกรมส่วนใหญ่ได้รับการออกแบบในลักษณะที่เป็นที่ถกเถียงกัน ในประวัติศาสตร์ของการเขียนโปรแกรมภาษามีภาษายอดนิยมบางอย่างที่สร้างขึ้นเช่นนั้น (เช่น Pascal, C, C ++, Java, VB คลาสสิก) แต่วันนี้ยังมีภาษาอื่น ๆ อีกมากมายที่ได้รับแฟน ๆ เพิ่มขึ้นเรื่อย ๆ ค่า
Doc Brown

คำตอบ:


58

บางภาษาเช่น Pythonสนับสนุนค่าที่ส่งคืนหลายค่าในขณะที่บางภาษาเช่น C #สนับสนุนพวกเขาผ่านไลบรารีฐานของพวกเขา

แต่โดยทั่วไปแม้ในภาษาที่สนับสนุนพวกเขาค่าการส่งคืนจำนวนมากไม่ได้ถูกใช้บ่อยเพราะมันเลอะเทอะ:

  • ฟังก์ชั่นที่ส่งกลับค่าหลายยากที่จะตั้งชื่ออย่างชัดเจน
  • ง่ายต่อการเข้าใจผิดลำดับของค่าส่งคืน

    (password, username) = GetUsernameAndPassword()  
    

    (ด้วยเหตุผลเดียวกันนี้หลายคนหลีกเลี่ยงการมีพารามิเตอร์มากเกินไปในฟังก์ชั่นบางคนถึงกับบอกว่าฟังก์ชั่นไม่ควรมีสองพารามิเตอร์ในประเภทเดียวกัน!)

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

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


50
มันยากที่จะบอกว่าการคืน tuple กำลังส่งคืนสิ่งต่าง ๆ มันกลับมาหนึ่งอันดับ รหัสที่คุณเขียนเพียงนำมันออกมาสะอาดโดยใช้น้ำตาลประโยค

10
@Lego: ฉันไม่เห็นความแตกต่าง - tuple คือการกำหนดค่าหลายค่า สิ่งที่คุณจะพิจารณา "ค่าตอบแทนหลาย" ถ้าไม่ใช่สิ่งนี้
BlueRaja - Danny Pflughoeft

19
มันแตกต่างหมอกจริงๆ แต่พิจารณา ()Tuple นั่นคือสิ่งหนึ่งหรือเป็นศูนย์? โดยส่วนตัวแล้วฉันจะพูดสิ่งหนึ่ง ฉันสามารถกำหนดได้ดีเหมือนที่ผมสามารถกำหนดx = () x = randomTuple()ในระยะหลังถ้า tuple กลับว่างเปล่าหรือไม่ก็ยังสามารถกำหนดกลับ tuple xหนึ่งไป

19
... ฉันไม่เคยอ้างว่าทูเปิลไม่สามารถใช้กับสิ่งอื่นได้ แต่การโต้เถียงว่า"Python ไม่สนับสนุนค่าส่งคืนหลายค่าสนับสนุน tuples"เป็นเพียงความอวดดีอย่างไร้จุดหมาย นี่ยังเป็นคำตอบที่ถูกต้อง
BlueRaja - Danny Pflughoeft

14
tuples หรือคลาสไม่เป็น "ค่าหลายค่า"
Andres F.

54

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

สิ่งนี้ไม่ได้เปลี่ยนแปลงเนื่องจากโปรแกรมเมอร์พบoutพารามิเตอร์ที่จะสร้างอย่างไม่น่าเชื่อที่มีประโยชน์ในชุดสถานการณ์จำลองที่ จำกัด เท่านั้น เช่นเดียวกับสิ่งอื่น ๆ การสนับสนุนไม่ได้อยู่ที่นั่นเพราะความต้องการ / ความต้องการไม่อยู่ที่นั่น


5
@FrustratedWithFormsDesigner - นี้ขึ้นมานิด ๆ หน่อย ๆ ในคำถามที่ผ่านมา ฉันสามารถนับจำนวนครั้งที่ฉันต้องการหลายเอาท์พุตในมือข้างหนึ่งใน 20 ปี
Telastyn

61
ฟังก์ชั่นในวิชาคณิตศาสตร์และฟังก์ชั่นในภาษาการเขียนโปรแกรมส่วนใหญ่เป็นสัตว์ที่แตกต่างกันสองอย่าง
tdammers

17
@ ผู้ส่งข่าวในช่วงแรก ๆ พวกเขาคิดคล้ายกันมาก Fortran, Pascal และชอบที่ได้รับอิทธิพลอย่างมากจากคณิตศาสตร์มากกว่าสถาปัตยกรรมคอมพิวเตอร์

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

16
@SteveEvers: เป็นเรื่องน่าเสียดายที่ชื่อ "ฟังก์ชั่น" เข้ามาแทนที่การเขียนโปรแกรมที่จำเป็นแทน "ขั้นตอน" หรือ "ขั้นตอน" ที่เหมาะสมกว่า ในฟังก์ชั่นการเขียนโปรแกรมฟังก์ชั่นคล้ายกับฟังก์ชั่นทางคณิตศาสตร์อย่างใกล้ชิดมากขึ้น
tdammers

35

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

สำหรับฟังก์ชั่นที่มีหลายค่า (ตัวอย่างเช่นรากที่สองของจำนวนเต็มบวกเป็นต้น) ก็เพียงพอแล้วที่จะส่งคืนคอลเลกชันหรือลำดับของค่า

สำหรับประเภทของฟังก์ชั่นที่คุณกำลังพูดถึง (เช่นฟังก์ชั่นที่คืนค่าหลายค่าของประเภทที่แตกต่างกัน ) ฉันเห็นว่ามันแตกต่างจากที่คุณดูเหมือนเล็กน้อย: ฉันเห็นความต้องการ / การใช้งาน params เป็นวิธีแก้ปัญหาสำหรับการออกแบบที่ดีขึ้นหรือ โครงสร้างข้อมูลที่มีประโยชน์มากขึ้น ตัวอย่างเช่นฉันต้องการถ้า*.TryParse(...)วิธีคืนMaybe<T>monad แทนที่จะใช้ param out คิดว่ารหัสนี้ใน F #:

let s = "1"
match tryParse s with
| Some(i) -> // do whatever with i
| None -> // failed to parse

การสนับสนุนคอมไพเลอร์ / IDE / การวิเคราะห์ดีมากสำหรับโครงสร้างเหล่านี้ สิ่งนี้จะช่วยแก้ปัญหา "ความต้องการ" ส่วนมากสำหรับการทำ params เพื่อความซื่อสัตย์อย่างสมบูรณ์ฉันไม่สามารถคิดวิธีอื่นใดนอกมือที่นี่จะไม่แก้ปัญหา

สำหรับสถานการณ์อื่น ๆ - สถานการณ์ที่ฉันจำไม่ได้ - ความเรียบง่ายของ Tuple


1
นอกจากนี้ฉันอยากจะสามารถเขียนใน C #: var (value, success) = ParseInt("foo");ซึ่งจะเป็นการตรวจสอบประเภทเวลารวบรวมเพราะ(int, bool) ParseInt(string s) { }มีการประกาศ ฉันรู้ว่าสิ่งนี้สามารถทำได้ด้วยยาชื่อสามัญ แต่ก็ยังจะทำให้การเพิ่มภาษาที่ดี
หน้าตาบูดบึ้งของ Despair

10
@GrimaceofDespair สิ่งที่คุณต้องการจริงๆคือการทำลายไวยากรณ์ไม่ใช่ค่าที่ส่งคืน
Domenic

2
@warren: ใช่ ดูที่นี่และทราบว่าโซลูชันดังกล่าวจะไม่ต่อเนื่อง: en.wikipedia.org/wiki/Well-definition
Steven Evers

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

1
@MichaelSiler (ฉันเห็นด้วยกับความคิดเห็นของคุณ) แต่โปรดทราบว่าอาร์กิวเมนต์สามารถย้อนกลับได้: "อาร์กิวเมนต์ที่ฟังก์ชั่นของโปรแกรมสามารถคืนค่าหลายค่าเพราะพวกเขาสามารถคืนค่า tuple เดียวไม่น่าสนใจมาก" :)
Andres F.

23

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


Ah! จุดรายละเอียดที่น่าสนใจมาก +1!
Steven Evers

นี่ควรเป็นคำตอบที่ยอมรับได้ โดยทั่วไปแล้วผู้คนจะคิดถึงเครื่องเป้าหมายเมื่อสร้างคอมไพเลอร์ การเปรียบเทียบอื่นคือสาเหตุที่เรามี int, float, char / string ฯลฯ เพราะนั่นคือสิ่งที่รองรับโดยเครื่องเป้าหมาย แม้ว่าเป้าหมายไม่ใช่โลหะเปลือย (เช่น jvm) คุณยังต้องการประสิทธิภาพที่ดีโดยไม่เลียนแบบมากเกินไป
imel96

26
... คุณสามารถกำหนดรูปแบบการโทรสำหรับส่งคืนค่าหลายค่าจากฟังก์ชันได้อย่างง่ายดายในแบบเดียวกับที่คุณสามารถกำหนดรูปแบบการโทรสำหรับส่งค่าหลายค่าไปยังฟังก์ชัน นี่ไม่ใช่คำตอบ -1
BlueRaja - Danny Pflughoeft

2
มันจะน่าสนใจที่จะทราบว่าสคริปแอคชั่นตามสแต็ก (JVM, CLR) เคยพิจารณา / อนุญาตให้ส่งคืนค่าหลายค่าหรือไม่ .. มันควรจะค่อนข้างง่าย ผู้ที่โทรเข้ามาจะต้องใส่ค่าจำนวนที่เหมาะสมเหมือนกดหมายเลขที่ถูกต้อง!
Lorenzo Dematté

1
@ David ไม่มีcdeclช่วยให้การ (ทฤษฎี) ไม่ จำกัด จำนวนของพารามิเตอร์(นั่นคือเหตุผลที่varargs ฟังก์ชั่นที่เป็นไปได้) แม้ว่าคอมไพเลอร์ C บางตัวอาจ จำกัด คุณไว้หลายสิบหรือหลายร้อยข้อโต้แย้งต่อฟังก์ชั่นซึ่งฉันคิดว่ายังมีเหตุผลมากกว่า -_-
BlueRaja - Danny Pflughoeft

18

กรณีการใช้งานจำนวนมากที่คุณจะต้องใช้ค่าส่งคืนหลายครั้งในอดีตไม่จำเป็นต้องใช้อีกต่อไปด้วยคุณสมบัติภาษาที่ทันสมัย ต้องการส่งคืนรหัสข้อผิดพลาดหรือไม่? Either<T, Throwable>โยนยกเว้นหรือกลับ ต้องการส่งคืนผลลัพธ์เสริมหรือไม่? Option<T>กลับ ต้องการส่งคืนหนึ่งในหลายประเภทหรือไม่ ส่งคืนEither<T1, T2>หรือเป็นสหภาพที่ติดแท็ก

และแม้ในกรณีที่คุณต้องการส่งคืนค่าหลายค่าอย่างแท้จริงภาษาสมัยใหม่มักจะสนับสนุนสิ่งอันดับ (tuples) หรือโครงสร้างข้อมูล (รายการ, อาร์เรย์, พจนานุกรม) หรือวัตถุบางอย่างรวมทั้งรูปแบบการผูกหรือการทำลายรูปแบบบางอย่าง ค่าหลายค่าของคุณเป็นค่าเดียวแล้วทำลายมันอีกครั้งเป็นค่าหลาย ๆ อย่าง

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

ทับทิม

def foo; return 1, 2, 3 end

one, two, three = foo

one
# => 1

three
# => 3

หลาม

def foo(): return 1, 2, 3

one, two, three = foo()

one
# >>> 1

three
# >>> 3

สกาล่า

def foo = (1, 2, 3)

val (one, two, three) = foo
// => one:   Int = 1
// => two:   Int = 2
// => three: Int = 3

Haskell

let foo = (1, 2, 3)

let (one, two, three) = foo

one
-- > 1

three
-- > 3

Perl6

sub foo { 1, 2, 3 }

my ($one, $two, $three) = foo

$one
# > 1

$three
# > 3

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

1
แต่ภาษาแบบไดนามิกเช่น Python หรือ Ruby เป็นอย่างไร สมมติว่าฉันเขียนบางอย่างเช่นsortฟังก์ชันMatlab : sorted = sort(array)ส่งคืนเฉพาะอาร์เรย์ที่เรียงลำดับ แต่[sorted, indices] = sort(array)ส่งคืนทั้งสองอย่าง วิธีเดียวที่ฉันสามารถคิดในหลามจะผ่านธงไปsortตามสายของหรือsort(array, nout=2) sort(array, indices=True)
gerrit

2
@ MikeCellini ฉันไม่คิดอย่างนั้น ฟังก์ชั่นสามารถบอกได้ว่ามีอาร์กิวเมนต์จำนวนเอาต์พุตที่ฟังก์ชันเรียกว่า ( [a, b, c] = func(some, thing)) และดำเนินการตามนั้นอย่างไร สิ่งนี้มีประโยชน์ตัวอย่างเช่นหากการคำนวณอาร์กิวเมนต์เอาต์พุตแรกนั้นราคาถูก แต่การคำนวณอาร์กิวเมนต์ที่สองนั้นมีราคาแพง ฉันไม่คุ้นเคยกับภาษาอื่นใดที่เทียบเท่ากับ Matlabs nargoutสามารถใช้งานได้
gerrit

1
@gerrit sorted, _ = sort(array)วิธีที่ถูกต้องในหลามคือการเขียนนี้:
Miles Rout

1
@MilesRout: และsortฟังก์ชั่นสามารถบอกได้ว่ามันไม่จำเป็นต้องคำนวณดัชนี? มันเจ๋งมากฉันไม่รู้
Jörg W Mittag

12

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

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

Perlเป็นภาษาอื่นที่บางครั้งอาจทำหน้าที่เหมือนฟังก์ชันส่งคืนค่าหลายค่าแม้ว่าโดยปกติแล้วจะถือว่าเป็นการส่งคืนรายการ วิธีแสดงรายการ"interpolate"ใน Perl ให้รายการเหมือนกับ(1, foo(), 3)ที่อาจมีองค์ประกอบ 3 อย่างที่คนส่วนใหญ่ที่ไม่รู้จัก Perl คาดหวัง แต่อาจมีองค์ประกอบเพียง 2 องค์ประกอบ 4 องค์ประกอบหรือองค์ประกอบอื่น ๆ อีกมากมายขึ้นอยู่กับfoo(). รายการใน Perl ถูกทำให้แบนเพื่อให้รายการไวยากรณ์ไม่ได้มีความหมายของรายการเสมอไป มันอาจเป็นเพียงส่วนหนึ่งของรายการขนาดใหญ่

อีกวิธีที่จะให้ฟังก์ชั่นคืนค่าหลายค่าจะต้องมีความหมายของการแสดงออกทางเลือกที่การแสดงออกใด ๆ สามารถมีหลายค่าและแต่ละค่าแสดงถึงความเป็นไป เอาx + 1อีกครั้ง แต่คราวนี้ลองนึกภาพว่าxมีสองค่า {3, 4} ดังนั้นค่าของx + 1จะเป็น {4, 5} และค่าของx + xจะเป็น {6, 8} หรืออาจจะ {6, 7, 8} xขึ้นอยู่กับว่าการประเมินผลอย่างใดอย่างหนึ่งได้รับอนุญาตให้ใช้ค่าหลาย ภาษาเช่นนั้นอาจถูกนำมาใช้โดยใช้การย้อนรอยเหมือนPrologใช้ในการตอบคำถามหลายข้อ

ในระยะสั้นการเรียกใช้ฟังก์ชั่นเป็นหน่วยไวยากรณ์เดียวและหน่วยไวยากรณ์เดียวมีค่าเดียวในความหมายการแสดงออกที่เราทุกคนรู้และชื่นชอบ ความหมายอื่น ๆ จะบังคับให้คุณทำสิ่งแปลก ๆ เช่น Perl, Prolog หรือ Forth


9

ตามคำแนะนำในคำตอบนี้มันเป็นเรื่องของการสนับสนุนฮาร์ดแวร์แม้ว่าประเพณีในการออกแบบภาษาก็มีบทบาท

เมื่อฟังก์ชันส่งคืนฟังก์ชันจะปล่อยให้ตัวชี้ไปยังวัตถุที่ส่งคืนในการลงทะเบียนที่ระบุ

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

ภาษาแรกเหล่านี้มีอิทธิพลอย่างมากต่อการออกแบบของภาษาที่ตามมาแม้ว่าจะมีเพียงค่าเดียวที่คืนค่า Lisp แต่ก็ไม่เคยรวบรวมความนิยมมากนัก

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

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

ภาษาส่วนใหญ่ที่แยกออกจากบรรทัดฐานนี้ไม่เคยได้รับความนิยมมากนักและดังนั้นจึงไม่เคยมีบทบาทที่แข็งแกร่งที่มีอิทธิพลต่อการตัดสินใจของนักออกแบบภาษา (แน่นอนว่าเป็นแรงบันดาลใจจากสิ่งที่พวกเขารู้)

ลองดูภาษาแอสเซมบลีกัน ก่อนอื่นมาดูที่6502ซึ่งเป็นไมโครโพรเซสเซอร์ 1975 ซึ่งถูกใช้อย่างมีชื่อเสียงโดยไมโครคอมพิวเตอร์ Apple II และ VIC-20 มันอ่อนแอมากเมื่อเทียบกับสิ่งที่ใช้ในเมนเฟรมและ minicomputers ของเวลาแม้ว่าทรงพลังเมื่อเทียบกับคอมพิวเตอร์เครื่องแรกของ 20, 30 ปีก่อนในตอนเช้าของภาษาโปรแกรม

ถ้าคุณดูที่คำอธิบายทางเทคนิคมันมี 5 รีจิสเตอร์พร้อมกับแฟล็กหนึ่งบิต การลงทะเบียน "เต็ม" เพียงอย่างเดียวคือ Program Counter (PC) - ที่ลงทะเบียนคะแนนไปยังคำสั่งถัดไปที่จะดำเนินการ รีจิสเตอร์อื่นโดยที่ตัวสะสม (A), สอง "ดัชนี" รีจิสเตอร์ (X และ Y) และตัวชี้สแต็ก (SP)

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

หากคุณดูโปรเซสเซอร์ที่เป็นแรงบันดาลใจ 6502, 6800จะมีการลงทะเบียนเพิ่มเติม, ดัชนีการลงทะเบียน (IX), กว้างเท่ากับ SP ซึ่งสามารถรับค่าจาก SP

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

SP + 8: param 2
SP + 6: param 1
SP + 4: return address
SP + 2: local 2
SP + 0: local 1

สามารถเรียกได้หลายครั้งเพราะพื้นที่ชั่วคราวทั้งหมดอยู่บนสแต็ก

8080 , ใช้ใน TRS-80 และโฮสต์ของ CP / M-ไมโครคอมพิวเตอร์ที่ใช้สามารถทำบางสิ่งบางอย่างที่คล้ายกับ 6800 โดยการผลักดัน SP ในกองแล้ว popping มันเกี่ยวกับการลงทะเบียนโดยทางอ้อม, HL

นี่เป็นวิธีการทั่วไปในการนำสิ่งต่าง ๆ มาใช้และได้รับการสนับสนุนมากขึ้นในตัวประมวลผลที่ทันสมัยยิ่งขึ้นด้วย Base Pointer ที่ทำให้การทิ้งตัวแปรท้องถิ่นทั้งหมดก่อนที่จะกลับมาง่าย

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

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

ลองดูภาษาที่อนุญาตการคืนค่าหลาย ๆ ค่า: Forth สิ่งที่ Forth ทำคือการรักษา stack stack (RP) และ data stack (SP) ที่แยกต่างหากเพื่อให้ฟังก์ชั่นทั้งหมดที่ต้องทำคือ pop พารามิเตอร์ทั้งหมดและเก็บค่าส่งคืนบนสแต็ก เนื่องจากสแต็กส่งคืนแยกจากกันจึงไม่สามารถเข้าถึงได้

ในฐานะคนที่เรียนรู้ภาษาแอสเซมบลีและ Forth ในช่วงหกเดือนแรกของประสบการณ์กับคอมพิวเตอร์ ตัวดำเนินการเช่น Forth's /modซึ่งคืนค่าการแบ่งจำนวนเต็มและส่วนที่เหลือดูเหมือนจะชัดเจน ในทางกลับกันฉันสามารถเห็นได้อย่างง่ายดายว่าคนที่มีประสบการณ์ในช่วงต้นคือ C ใจพบแนวคิดที่แปลก: มันขัดกับความคาดหวังที่ฝังแน่นของพวกเขาในสิ่งที่ "ฟังก์ชั่น" คืออะไร

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

ดังนั้นเราจึงมีการรวมตัวกันของปัจจัยที่คณิตศาสตร์มีอิทธิพลต่อการออกแบบภาษายุคแรก ๆ ที่ข้อ จำกัด ของฮาร์ดแวร์กำหนดสิ่งที่นำไปปฏิบัติได้ง่ายและที่ภาษายอดนิยมมีอิทธิพลต่อการพัฒนาฮาร์ดแวร์ (เครื่อง Lisp และโปรเซสเซอร์จากเครื่อง


@gnat การใช้ "เพื่อแจ้ง" ตามที่ "เพื่อให้คุณภาพที่สำคัญ" เป็นความตั้งใจ
Daniel C. Sobral

อย่าลังเลที่จะย้อนกลับหากคุณรู้สึกถึงสิ่งนี้มาก ต่อการอ่านของฉันอิทธิพลพอดีดีกว่าเล็กน้อยที่นี่: "มีผลกระทบต่อ ... ในทางที่สำคัญ"
ริ้น

1
+1 ตามที่ชี้ให้เห็นว่าการลงทะเบียนกระจัดกระจายของซีพียูยุคแรก ๆ เมื่อเทียบกับซีรีส์ร่วมสมัยที่มีการลงทะเบียนมากมาย (ใช้ใน ABIs จำนวนมากเช่น x64 abi) อาจเป็นตัวเปลี่ยนเกมและเหตุผลที่น่าสนใจในปัจจุบัน ทุกวันนี้อาจเป็นเพียงเหตุผลทางประวัติศาสตร์
BitTickler

ฉันไม่เชื่อว่าไมโครซีพียู 8 บิตในช่วงต้นมีอิทธิพลอย่างมากต่อการออกแบบภาษาและสิ่งที่คิดว่าจำเป็นสำหรับการประชุม C หรือ Fortran ในสถาปัตยกรรมใด ๆ Fortran ถือว่าคุณสามารถผ่าน args อาร์เรย์ (ตัวชี้เป็นหลัก) ดังนั้นคุณมีปัญหาการใช้งานที่สำคัญสำหรับ Fortran ปกติบนเครื่องเช่น 6502 เนื่องจากการขาดโหมดการกำหนดแอดเดรสสำหรับตัวชี้ + ดัชนีเช่นที่กล่าวถึงในคำตอบของคุณและทำไมคอมไพเลอร์ C ถึง Z80 จึงผลิตรหัสไม่ดี ใน retrocomputing.SE
Peter Cordes

Fortran เช่น C ถือว่าคุณสามารถส่งจำนวน args ได้ตามอำเภอใจและมีการเข้าถึงแบบสุ่มไปยังพวกมันและจำนวนคนในพื้นที่โดยพลการใช่ไหม? ตามที่คุณเพิ่งอธิบายคุณไม่สามารถทำได้อย่างง่ายดายใน 6502 เนื่องจากการกำหนดที่อยู่แบบสัมพันธ์กับสแต็กไม่ใช่เรื่องเว้นแต่คุณจะละทิ้งการแสดงความเห็นซ้ำ คุณสามารถเก็บไว้ในที่เก็บแบบคงที่ หากคุณสามารถส่งรายการ ARG โดยพลการคุณสามารถเพิ่มพารามิเตอร์พิเศษที่ซ่อนไว้สำหรับค่าส่งคืน (เช่นนอกเหนือจากแรก) ที่ไม่พอดีกับการลงทะเบียน
Peter Cordes

7

ภาษาที่ใช้งานได้ที่ฉันรู้จักสามารถคืนค่าหลาย ๆ ค่าได้อย่างง่ายดายผ่านการใช้สิ่งอันดับ (ในภาษาที่พิมพ์แบบไดนามิกคุณยังสามารถใช้รายการได้) Tuples รองรับภาษาอื่นด้วย:

f :: Int -> (Int, Int)
f x = (x - 1, x + 1)

// Even C++ have tuples - see Boost.Graph for use
std::pair<int, int> f(int x) {
  return std::make_pair(x - 1, x + 1);
}

ในตัวอย่างข้างต้นfเป็นฟังก์ชันที่คืนค่า 2 int

ในทำนองเดียวกัน ML, Haskell, F # และอื่น ๆ สามารถกลับโครงสร้างข้อมูลได้ (พอยน์เตอร์อยู่ในระดับต่ำเกินไปสำหรับภาษาส่วนใหญ่) ฉันไม่เคยได้ยินภาษา GP สมัยใหม่ที่มีข้อ จำกัด ดังกล่าว:

data MyValue = MyValue Int Int

g :: Int -> MyValue
g x = MyValue (x - 1, x + 1)

ในที่สุดoutพารามิเตอร์สามารถจำลองได้แม้ในภาษาที่ใช้งานIORefได้ มีสาเหตุหลายประการที่ไม่มีการสนับสนุนพื้นเมืองสำหรับตัวแปรออกในภาษาส่วนใหญ่:

  • ความหมายไม่ชัดเจน : ฟังก์ชันต่อไปนี้พิมพ์ 0 หรือ 1 หรือไม่? ฉันรู้ภาษาที่จะพิมพ์ 0 และคนที่จะพิมพ์ 1 มีประโยชน์ทั้งสองอย่าง (ทั้งในแง่ของประสิทธิภาพและการจับคู่รูปแบบจิตของโปรแกรมเมอร์):

    int x;
    
    int f(out int y) {
      x = 0;
      y = 1;
      printf("%d\n", x);
    }
    f(out x);
    
  • เอฟเฟ็กต์ที่ไม่แปล : ดังตัวอย่างข้างต้นคุณจะพบว่าคุณมีสายโซ่ยาวและฟังก์ชั่นด้านในมีผลต่อสถานะโลก โดยทั่วไปแล้วมันทำให้ยากที่จะให้เหตุผลเกี่ยวกับข้อกำหนดของฟังก์ชั่นและถ้าการเปลี่ยนแปลงนั้นถูกกฎหมาย เนื่องจากกระบวนทัศน์สมัยใหม่ส่วนใหญ่พยายาม จำกัด ผลกระทบ (encapsulation ใน OOP) หรือกำจัดผลข้างเคียง (การโปรแกรมเชิงปฏิบัติ) มันขัดแย้งกับกระบวนทัศน์เหล่านั้น

  • การซ้ำซ้อน : หากคุณมีสิ่งอันดับคุณมี 99% ของการทำงานของoutพารามิเตอร์และการใช้สำนวน 100% หากคุณเพิ่มพอยน์เตอร์เข้ากับมิกซ์นั้นคุณจะครอบคลุม 1%

ฉันมีปัญหาในการตั้งชื่อภาษาหนึ่งซึ่งไม่สามารถคืนค่าหลายค่าโดยใช้ tuple คลาสหรือoutพารามิเตอร์ (และในกรณีส่วนใหญ่อนุญาตให้ใช้วิธีเหล่านี้อย่างน้อย 2 วิธี)


+1 สำหรับการกล่าวถึงวิธีที่ภาษาที่ใช้งานได้จัดการกับสิ่งนี้ได้อย่างหรูหราและไม่เจ็บปวด
Andres F.

1
ในทางเทคนิคคุณยังคงส่งคืนค่าเดียว: D (เป็นเพียงว่าค่าเดียวนี้มีความสำคัญที่จะย่อยสลายเป็นหลายค่า)
Thomas Eding

1
ฉันจะบอกว่าพารามิเตอร์ที่มีความหมาย "ออก" จริงควรทำตัวเป็นคอมไพเลอร์ชั่วคราวซึ่งได้รับการคัดลอกไปยังปลายทางเมื่อวิธีการออกจากปกติ; หนึ่งที่มีความหมาย "inout" ตัวแปรควรทำหน้าที่เป็นคอมไพเลอร์ชั่วคราวซึ่งโหลดจากตัวแปรที่ส่งผ่านในรายการและเขียนกลับเมื่อออก; สิ่งที่มีความหมาย "ref" ควรทำตัวเป็นนามแฝง พารามิเตอร์ที่เรียกว่า "out" ของ C # นั้นเป็นพารามิเตอร์ "ref" จริง ๆ และมีพฤติกรรมเช่นนี้
supercat

1
tuple "วิธีแก้ปัญหา" ยังไม่มาฟรี มันบล็อกโอกาสในการเพิ่มประสิทธิภาพ หากมี ABI อยู่ซึ่งอนุญาตให้ส่งคืนค่า N ที่จะส่งคืนในการลงทะเบียน CPU ตัวแปลภาษาสามารถปรับให้เหมาะสมจริง ๆ แทนที่จะสร้างอินสแตนซ์ของ tuple และสร้างมันขึ้นมา
BitTickler

1
@BitTickler ไม่มีอะไรป้องกันการคืนค่า n ฟิลด์แรกของโครงสร้างที่จะถูกส่งผ่านโดยรีจิสเตอร์ถ้าคุณควบคุม ABI
Maciej Piechotka

6

ฉันคิดว่ามันเป็นเพราะการแสดงออก(a + b[i]) * cเช่น

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

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


4

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


3
คุณสามารถยกตัวอย่างที่ที่ผลตอบแทนหลายรายการให้รหัสที่ชัดเจนและ / หรือมีประสิทธิภาพมากขึ้นได้หรือไม่
Steven Evers

3
ตัวอย่างเช่นในการเขียนโปรแกรม C ++ COM ฟังก์ชั่นจำนวนมากมีหนึ่ง[out]พารามิเตอร์ แต่แทบทั้งหมดส่งกลับHRESULT(รหัสข้อผิดพลาด) มันจะค่อนข้างใช้งานได้จริงเพียงแค่จับคู่ที่นั่น ในภาษาที่มีการรองรับสิ่งอันดับเช่น Python สิ่งนี้จะถูกนำไปใช้ในโค้ดที่ฉันได้เห็น
Felix Dombek

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

3
หลายครั้งที่คุณจบการเข้ารหัสข้อมูลเป็นค่าตอบแทนในรูปแบบที่ไม่ชัดเจน - เช่น; ค่าลบคือรหัสข้อผิดพลาดค่าบวกคือผลลัพธ์ จุ๊บ การเข้าถึงตารางแฮชมันจะเลอะเทอะเสมอเพื่อระบุว่ารายการนั้นถูกค้นพบและส่งคืนสินค้าหรือไม่
ddyer

@SteveEvers Matlab ฟังก์ชั่นได้ตามปกติเรียงลำดับอาร์เรย์:sort sorted_array = sort(array)บางครั้งฉันก็ต้องการดัชนีที่สอดคล้องกัน[sorted_array, indices] = sort(array)ด้วย: . บางครั้งฉันต้องการดัชนีเท่านั้น : [~, indices]= sort (array) . The function sort` สามารถบอกได้ว่าต้องการอากิวเมนต์เอาต์พุตจำนวนเท่าใดดังนั้นหากต้องการงานเพิ่มเติมสำหรับ 2 เอาต์พุตเมื่อเทียบกับ 1 ก็สามารถคำนวณเอาท์พุทเหล่านั้นหากจำเป็นเท่านั้น
gerrit

2

ในภาษาส่วนใหญ่ที่รองรับฟังก์ชั่นคุณสามารถใช้การเรียกใช้ฟังก์ชันได้ทุกที่ที่สามารถใช้ตัวแปรประเภทนั้น: -

x = n + sqrt(y);

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


5
อย่าใช้ฟังก์ชั่นที่ไม่เหมาะสม สิ่งนี้ไม่แตกต่างจาก "ปัญหา" ที่เกิดจากฟังก์ชั่นที่ไม่คืนค่าใด ๆ หรือคืนค่าที่ไม่ใช่ตัวเลข
ddyer

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

และแม้กระทั่งเมื่อพวกเขาไม่ได้เช่นเดียวกับงูใหญ่เปิดออก tuple คุณสามารถเลือกที่หนึ่งที่คุณต้องการ:foo()[0]
Izkata

ถ้าฟังก์ชันส่งคืนค่า 2 ประเภทนั้นจะเป็นประเภทส่งคืนค่า 2 ไม่ใช่ค่าเดียว ภาษาการเขียนโปรแกรมไม่ควรอ่านใจ
Mark E. Haase

1

ฉันแค่ต้องการสร้างคำตอบของฮาร์วีย์ เดิมทีฉันพบคำถามนี้ในเว็บไซต์ข่าวเทคโนโลยี (arstechnica) และพบคำอธิบายที่น่าอัศจรรย์ที่ฉันรู้สึกว่าตอบคำถามหลักของคำถามนี้และขาดคำตอบอื่น ๆ ทั้งหมด (ยกเว้นฮาร์วีย์):

ต้นกำเนิดของการส่งคืนจากฟังก์ชั่นเดียวอยู่ในรหัสเครื่อง ที่ระดับรหัสเครื่องฟังก์ชั่นสามารถคืนค่าในการลงทะเบียน A (ตัวสะสม) ค่าส่งคืนอื่น ๆ จะอยู่ในสแต็ก

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

มันเหมือนกับถามว่าทำไมการมอบหมายให้เป็นหนึ่งตัวแปรในแต่ละครั้ง คุณสามารถมีภาษาที่อนุญาตให้ a, b = 1, 2 ได้ แต่มันจะจบลงที่ระดับรหัสเครื่องเป็น a = 1 ตามด้วย b = 2

มีเหตุผลในการสร้างภาษาการเขียนโปรแกรมมีรูปร่างหน้าตาบางอย่างกับสิ่งที่จะเกิดขึ้นจริงเมื่อรหัสรวบรวมและทำงาน


หากภาษาระดับต่ำเช่น C สนับสนุนค่าส่งคืนหลายค่าเป็นคุณลักษณะชั้นหนึ่งแบบแผนการโทร C จะรวมการลงทะเบียนหลายค่าที่กลับมาเช่นเดียวกับที่มีการลงทะเบียนสูงสุด 6 รายการสำหรับการส่งผ่านฟังก์ชันเลขจำนวนเต็ม / ตัวชี้ 64 System V ABI (อันที่จริงแล้ว x86-64 SysV จะคืนค่าโครงสร้างสูงถึง 16 ไบต์ที่บรรจุอยู่ในคู่ของ RDX: RAX register pair นี่เป็นสิ่งที่ดีถ้าโครงสร้างเพิ่งโหลดและจะถูกเก็บไว้ แต่ค่าใช้จ่ายเพิ่มเติมเมื่อเทียบกับการแยกสมาชิกใน regs แยก หากพวกเขากำลังแคบกว่า 64 บิต).
ปีเตอร์ Cordes

การประชุมที่ชัดเจนจะเป็น RAX จากนั้น regs ผ่าน ARG (RDI, RSI, RDX, RCX, R8, R9) หรือในการประชุม Windows x64, RCX, RDX, R8, R9 แต่เนื่องจาก C ไม่มีค่าการส่งคืนหลายค่าแบบดั้งเดิม C ABI / การเรียกการประชุมจะระบุการลงทะเบียนการกลับมาจำนวนมากสำหรับจำนวนเต็มกว้างและโครงสร้างบางอย่างเท่านั้น ดูขั้นตอนการเรียกมาตรฐานสำหรับสถาปัตยกรรมARM®: 2 แยกจากกัน แต่ค่าส่งคืนที่เกี่ยวข้องสำหรับตัวอย่างของการใช้ wide int เพื่อรับคอมไพเลอร์เพื่อสร้าง asm ที่มีประสิทธิภาพสำหรับการรับ 2 คืนค่าบน ARM
Peter Cordes

-1

มันเริ่มต้นด้วยคณิตศาสตร์ FORTRAN ชื่อสำหรับ "การแปลสูตร" เป็นคอมไพเลอร์ตัวแรก FORTRAN เป็นและมุ่งเน้นไปที่ฟิสิกส์ / คณิตศาสตร์ / วิศวกรรม

COBOL เกือบแก่แล้วไม่มีค่าตอบแทนชัดเจน มันแทบจะไม่มีรูทีนย่อย ตั้งแต่นั้นมาความเฉื่อยส่วนใหญ่

ไปยกตัวอย่างเช่นมีค่าที่ส่งคืนหลายและผลที่ได้คือการทำความสะอาดและคลุมเครือน้อยกว่าการใช้ "ออก" พารามิเตอร์ หลังการใช้งานเล็กน้อยมันเป็นธรรมชาติและมีประสิทธิภาพ ฉันแนะนำให้คืนค่าหลาย ๆ ค่าสำหรับภาษาใหม่ทั้งหมด บางทีสำหรับภาษาเก่าด้วย


4
สิ่งนี้ไม่ตอบคำถามที่ถาม
gnat

@gnat สำหรับฉันมันเป็นคำตอบ หนึ่งใน OTOH ต้องมีภูมิหลังเพื่อที่จะเข้าใจและบุคคลนั้นมีแนวโน้มที่จะไม่ถามคำถาม ...
Netch

@ ดึงหนึ่งแทบจะไม่ต้องการพื้นหลังมากในการคิดว่างบเช่น"FORTRAN ... เป็นคอมไพเลอร์แรก"เป็นระเบียบทั้งหมด มันไม่ผิดเลย เช่นเดียวกับส่วนที่เหลือของ "คำตอบ" นี้
gnat

linkกล่าวว่ามีความพยายามในการคอมไพเลอร์ก่อนหน้านี้ แต่ที่ "ทีม FORTRAN นำโดย John Backus ที่ IBM ได้รับเครดิตโดยทั่วไปว่าได้แนะนำคอมไพเลอร์ฉบับสมบูรณ์ครั้งแรกในปี 1957" คำถามที่ถามคือทำไมมีเพียงคำถามเดียว ที่ผมกล่าวว่า. ส่วนใหญ่เป็นคณิตศาสตร์และความเฉื่อย คำจำกัดความทางคณิตศาสตร์ของคำว่า "ฟังก์ชั่น" ต้องการค่าผลลัพธ์หนึ่งค่า ดังนั้นมันจึงเป็นรูปแบบที่คุ้นเคย
RickyS

-2

อาจเป็นเรื่องเกี่ยวกับมรดกของวิธีการเรียกใช้ฟังก์ชันในคำสั่งเครื่องโปรเซสเซอร์และความจริงที่ว่าภาษาการเขียนโปรแกรมทั้งหมดได้มาจากรหัสเครื่อง: ตัวอย่างเช่น C -> Assembly -> Machine

โปรเซสเซอร์ดำเนินการเรียกใช้ฟังก์ชันอย่างไร

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

ทีนี้เหตุใดโปรเซสเซอร์จึงถูกออกแบบด้วยวิธีนี้ ... มันอาจเป็นคำถามของข้อ จำกัด ด้านทรัพยากร

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