Giá trị trả về được đặt bằng cách gán cho tên của hàm trong (các) ngôn ngữ nào?

Jan 18 2021

Trong câu hỏi Stack Overflow này , mã gốc đã mắc lỗi khi sử dụng tên hàm làm biến và gán giá trị trả về cho nó. Một người bình luận đã đề cập rằng anh ta đã từng sử dụng một ngôn ngữ mà đây là cách bạn trả về giá trị từ các hàm. Nhận xét có nội dung "Tôi biết tôi đã từng sử dụng một ngôn ngữ mà giá trị trả về của một hàm cần được gán cho tên của hàm. Nó quá cổ và lỗi thời, tôi thậm chí không thể nhớ đó là ngôn ngữ nào."

Điều đó nghe có vẻ quen thuộc với tôi, nhưng tôi cũng không thể nhớ đó là ngôn ngữ gì.

Có ai có trí nhớ tốt hơn chúng tôi và có thể cho chúng tôi biết đó là ngôn ngữ nào không?

Trả lời

50 WillHartung Jan 18 2021 at 22:45

Pascal làm được điều này, tôi không biết những người khác. Không biết liệu việc luyện tập có tiến triển với các ngôn ngữ Wirth khác hay không.

50 OmarL Jan 18 2021 at 23:14

Các ngôn ngữ trong họ Visual Basic thực hiện chính xác điều này. Điều này bao gồm VBScript, VBA, Visual Basic và các phiên bản cũ hơn. Tôi tin rằng chúng kế thừa "tính năng" từ QBASIC. Ví dụ

Public Function AddTwo(something as Integer)
    AddTwo = something + 2
End Function
29 scruss Jan 18 2021 at 23:03

Fortran, chắc chắn:

      PROGRAM TRIANG 
      WRITE(UNIT=*,FMT=*)'Enter lengths of three sides:' 
      READ(UNIT=*,FMT=*) SIDEA, SIDEB, SIDEC 
      WRITE(UNIT=*,FMT=*)'Area is ', AREA3(SIDEA,SIDEB,SIDEC) 
      END 

      FUNCTION AREA3(A, B, C) 
*Computes the area of a triangle from lengths of sides 
      S = (A + B + C)/2.0 
      AREA3 = SQRT(S * (S-A) * (S-B) * (S-C)) 
      END

(từ Hướng dẫn lập trình viên chuyên nghiệp của Clive G. Page cho Fortran77 ).

Nó cũng được định nghĩa theo cách đó trong tiêu chuẩn Fortran ANSI X 3.9 1966 Fortran 66 .

22 user3840170 Jan 18 2021 at 23:54

Những ngôn ngữ sớm nhất mà tôi có thể tìm thấy là FORTRAN II và ALGOL 58, cả hai đều được xuất bản vào cùng năm 1958; mặc dù bản gốc FORTRAN (1956) cũng có thể được đưa vào.

Đối với FORTRAN, trang đầu tiên của chương hướng dẫn sử dụng bao gồm các chức năng chứa ví dụ này (trang 27):

FUNCTION AVRG (ALIST, N)
DIMENSION ALIST (500)
SUM = ALIST (1)
DO 10 I=2, N
SUM = SUM + ALIST (I)
AVRG = SUM / FLOATF (N)
RETURN
END (2, 2, 2, 2, 2)

FORTRAN II cũng bao gồm một cú pháp hàm khác (trang 10), định nghĩa hàm một dòng, được kế thừa từ bộ tiền xử lý của nó:

FIRSTF(X) = A*X + B

Không khó để thấy cú pháp trước đây là một phần mở rộng tự nhiên của cú pháp sau, đến lượt nó đến từ cách sử dụng toán học.

ALGOL 58 , tương tự như FORTRAN, xác định cả hai 'chức năng' một dòng:

Một khai báo hàm khai báo một biểu thức đã cho là một hàm của một số biến nhất định của nó. Qua đó, khai báo đưa ra (đối với một số hàm đơn giản nhất định) quy tắc tính toán để gán giá trị cho hàm (xem hàm ) bất cứ khi nào hàm này xuất hiện trong một biểu thức.

Dạng: Δ ~ I n (I, I, ~, I): = E trong đó I là các định danh và E là một biểu thức, trong số các thành phần của nó, có thể chứa các biến đơn giản được đặt tên bởi các định danh xuất hiện trong dấu ngoặc đơn.

và 'thủ tục', tương đương với định nghĩa ngày nay về hàm (ít nhất là trong các ngôn ngữ lập trình mệnh lệnh / thủ tục). Giá trị trả về được chỉ ra như sau (tr. 19):

Đối với mỗi thủ tục đầu ra đơn lẻ I (P i ) được liệt kê trong tiêu đề, một giá trị phải được gán trong thủ tục bằng câu lệnh gán “I: = E” trong đó tôi là định danh đặt tên cho thủ tục đó.

Các cú pháp này sau đó đã được sử dụng bởi một số phương ngữ của BASIC (ở dạng DEF FNvà sau này FUNCTION) và con cháu của ALGOL là Pascal: trong các trình biên dịch Pascal của Borland, gán cho tên hàm là cú pháp duy nhất được hỗ trợ trước khi Resultbiến trong Delphi 1.0 ra đời.

Nó có lẽ là Pascal mà người bình luận được đề cập nhớ; một số trường đại học vẫn dạy lập trình trong đó, và thường bám vào bản gốc, chuẩn đa dạng, thay vì các phương ngữ mở rộng hiện đại như Object Pascal. (Đây thực sự không phải là một phần của câu hỏi, nhưng tôi cho rằng sự hiểu lầm của người hỏi StackOverflow cũng xuất phát từ đó.)

17 Raffzahn Jan 19 2021 at 00:28

TL; DR:

Tôi muốn nói, rất có thể đó là PASCAL mà bạn còn nhớ, vì nó khá phổ biến vào đầu những năm 80, được sử dụng trong các khóa học Đại học từ những năm 80 đến những năm 90 và vẫn có một số học bổng sau đó, đáng chú ý nhất là Delphi.


Một số lịch sử

Ý tưởng cơ bản là tên hàm không chỉ đã được đặt trước, vì vậy không cần phải nghĩ ra bất kỳ điều gì khác biệt, và sử dụng nó là một tuyên bố rõ ràng rằng đây là kết quả. Nó cũng đơn giản hóa thiết kế trình biên dịch, vì một mục dữ liệu chuyên dụng có thể được cấp phát trong quy ước gọi.

Về cơ bản có hai dòng di sản, FORTRAN và ALGOL.

Đối với cả hai con cháu của họ đã giữ nó, như

  • một số biến thể CƠ BẢN từ FORTRAN và
  • Pascal và Modula từ ALGOL.

Những người khác đã bỏ nó, như ALGOL theo dõi

  • BCPL, đã giới thiệu return()cú pháp,

khá phổ biến ngày nay vì C đã lấy nó từ BCPL.

Ý tưởng ngôn ngữ giống như gen nhảy giữa các vật chủ. Ví dụ ADA, theo nhiều cách, một đứa cháu ALGOL / PASCAL, cũng chuyển sang sử dụng một returnphần tử.

Ông ngoại FORTRAN, trong nhiều năm, đã thay đổi cách trả về kết quả hàm.

  • Ban đầu, kết quả của một hàm được gán cho mã định danh của hàm
  • với FORTRAN 90, định nghĩa rõ ràng về tên trả về trong phần đầu hàm đã được giới thiệu.

Trong khi về cơ bản đây chỉ là đường cú pháp, nó có một sự thay đổi trong phong cách. Lý do được áp dụng là với các cấu trúc đệ quy Foo = Foo(x-1)sẽ trông kỳ lạ. Nhưng tôi đoán đó là cách giải thích.

Điều thú vị ở đây là FORTRAN II năm 1958 đã giới thiệu một RETURNtuyên bố trong nỗ lực bổ sung lập trình thủ tục, nhưng cách sử dụng của nó chỉ đơn giản là để trả lại thực thi cho người gọi, giá trị trả về phải được đặt riêng biệt.

12 alephzero Jan 18 2021 at 23:02

Fortran đã sử dụng cú pháp này, từ phiên bản đầu tiên có các chức năng cho đến Fortran 2008 và hơn thế nữa.

Tuy nhiên Fortran 2008 có một tùy chọn (thậm chí còn khó hiểu hơn?), Nơi bạn có thể khai báo một tên biến khác được sử dụng để trả về một giá trị hàm! Ví dụ

function xyz(argument) result(answer)
...
answer = 42
...
end function xyz

thay vì kiểu cũ

...
xyz = 42
...
9 another-dave Jan 19 2021 at 01:02

Algol 60 cho một.

Đây là những từ có liên quan từ Báo cáo sửa đổi về Ngôn ngữ thuật toán Algol 60 .

5.4.4. Giá trị của người chỉ định chức năng.

Đối với một khai báo thủ tục để xác định giá trị của một bộ chỉ định hàm, trong cơ quan khai báo thủ tục, phải xảy ra một hoặc nhiều câu lệnh gán rõ ràng với mã định danh thủ tục ở một phần bên trái; ít nhất một trong số này phải được thực thi và kiểu được liên kết với mã định danh thủ tục phải được khai báo thông qua sự xuất hiện của bộ khai báo kiểu như là ký hiệu đầu tiên của khai báo thủ tục. Giá trị cuối cùng được chỉ định như vậy được sử dụng để tiếp tục đánh giá biểu thức trong đó hàm chỉ định xuất hiện.

Bất kỳ sự xuất hiện nào của mã định danh thủ tục trong phần thân của thủ tục khác với phần bên trái của câu lệnh gán biểu thị việc kích hoạt thủ tục.

Câu cuối cùng rất quan trọng - nó cho thấy rằng tên của thủ tục kiểu (hàm) không được coi là 'giống như' một biến trong phần thân của thủ tục (hàm); đúng hơn, nó chỉ là nhiệm vụ được phân biệt đặc biệt.

Trong Algol 60, một lệnh gọi đến một hàm không nhận đối số sẽ không được theo sau bởi dấu ngoặc đơn trống: do đó n := readthay vì n := read().

Câu cuối cùng cũng nổi tiếng là câu có các thủ tục đệ quy vào ngôn ngữ. Nhưng đó không phải là điều sai trái đối với câu trả lời này.

6 StephenKitt Jan 18 2021 at 23:03

BASIC là một ngôn ngữ khác có các hàm trong đó một số phương ngữ đã sử dụng phép gán cho tên hàm để cung cấp giá trị trả về. Các phương ngữ đầu tiên tương tự như các hàm đơn dòng của Fortran:

DEF FND(x) = x*x

Nhưng các phương ngữ sau này cho phép các biến thể phức tạp hơn, tương tự như các hàm đa dòng của Fortran :

DEF FNPeekWord& (A&)
  FNPeekWord& = PEEK(A&) + 256& * PEEK(A& + 1)
END DEF
5 LyndonWhite Jan 19 2021 at 20:38

MATLAB / Octave cũng thực hiện điều này.

Đó là từ năm 1984; vì vậy không phải là cũ như một số người khác.

Nó có lẽ đang bắt chước Fortran, vì nó đã được coi là một công cụ cấp cao. Trên đầu trang của các thư viện Fortran như Linpack và Eispack.

5 chasly-supportsMonica Jan 19 2021 at 21:29

Tôi tin rằng SNOBOL4 đã làm được điều này. http://berstis.com/greenbook.pdf

Sau đây là một ví dụ về định nghĩa và sử dụng một hàm để tính thừa số của các số:

   DEFINE('FACT(N)') :(SKIPFCN)
 * Set value to 1
 FACT      FACT = 1
 * Return 1 if N<2
 * Return N*((N-1)!) with recursive call
   FACT = GT(N,1) FACT(N - 1) * N :(RETURN)
 SKIPFCN
   OUTPUT = '5 factorial is ' FACT(5)

http://berstis.com/s4ref/prim3e.htm

4 d3jones Jan 19 2021 at 07:12

Verilog (1995/2001) cũng trả về bằng cách gán cho biến ngầm định. SystemVerilog đã thêm câu lệnh "return" nhưng phép gán cổ điển vẫn có sẵn.

1 arne Jan 21 2021 at 03:02

Haskell (từ 1990) cũng làm điều này:

doubleMe x = x + x  

xác định một hàm doubleMecủa một tham số xvà gán phần thân hàm x+xcho nó, hãy xem bài học tuyệt vời Learn You A Haskell For Great Good

mathrick Jan 19 2021 at 13:08

Pascal là một trong những mà cá nhân tôi đã sử dụng để làm điều đó. Lisp phổ biến kinda-sorta-but-not-thực sự làm được điều đó, trong đó các giá trị trả về hầu như luôn ngầm định (ví dụ: mọi câu lệnh đều có một giá trị và giá trị cuối cùng trong một khối là giá trị trả về của khối), vì vậy bạn rất hiếm khi thấy một tuyên bố trở lại rõ ràng, nhưng khi bạn cần phải trả lại một giá trị và không thể sử dụng cách tiềm ẩn, cách để làm điều đó là sử dụng RETURN-FROM[*] tuyên bố, như vậy: (return-from function-name value).

[*] Cũng có một RETURNcâu lệnh, nhưng nó là cách viết tắt của (return-from nil value)và sẽ không có tác dụng tạo VALUEgiá trị của hàm mà nó được thực thi. Đó là một cạm bẫy lớn cho những người mới đến từ C và các hậu duệ của nó.