Tại sao nhiều ngôn ngữ không hỗ trợ tham số được đặt tên? [đóng cửa]


14

Tôi chỉ nghĩ rằng việc đọc mã sẽ dễ dàng hơn bao nhiêu nếu, khi gọi một hàm, bạn có thể viết:

doFunction(param1=something, param2=somethingElse);

Tôi không thể nghĩ ra bất kỳ nhược điểm nào và nó sẽ giúp mã dễ đọc hơn rất nhiều. Tôi biết bạn có thể truyền một mảng làm đối số duy nhất và có các khóa mảng làm tên tham số, tuy nhiên điều đó vẫn không thể đọc được.

Có bất lợi về điều này mà tôi đang thiếu? Nếu không, tại sao nhiều ngôn ngữ không cho phép điều này?


1
bạn có thể đưa ra một ngôn ngữ nên (và có thể) có các tham số được đặt tên nhưng không có ngôn ngữ đó không?
Bryan Chen

1
Tôi nghĩ rằng nó quá dài dòng trong nhiều trường hợp. Theo như tôi đã thấy, một ngôn ngữ có sử dụng ngôn ngữ này hay không có xu hướng phụ thuộc vào việc liệu nó sẽ tác động đến ngôn ngữ tích cực hay tiêu cực. Hầu hết các ngôn ngữ kiểu C làm tốt mà không có nó. Trong trường hợp của họ, dù sao đi nữa, mọi thứ đang diễn ra khá rõ ràng và sự vắng mặt của nó thực sự giúp giảm bớt sự lộn xộn nói chung.
Panzercrisis

2
@SteveFallows: "Thành ngữ tham số được đặt tên" có vẻ như là một tên lạ để định nghĩa API thông thạo (như được đặt tên bởi Martin Fowler) trên một lớp xây dựng. Bạn có thể tìm thấy các ví dụ về cùng một mẫu trong một trong những mục đầu tiên của Java hiệu quả của Joshua Bloch .
jhominal

3
Đây không nhất thiết là một câu hỏi dựa trên ý kiến
Catskul

2
Đã đồng ý. "Tại sao nhiều ngôn ngữ không hỗ trợ các tham số được đặt tên?" là một câu hỏi về lịch sử của ngôn ngữ hơn là ý kiến. Sẽ là ý kiến ​​nếu ai đó hỏi nó là "Không được đặt tên tham số tốt hơn?".
Andy Fleming

Câu trả lời:


14

Việc chỉ định tên tham số không phải lúc nào cũng làm cho mã dễ đọc hơn: như một ví dụ, bạn muốn đọc min(first=0, second=1)hay hơn min(0, 1)?

Nếu bạn chấp nhận đối số của đoạn trước, thì rõ ràng việc chỉ định tên tham số không nên bắt buộc. Tại sao tất cả các ngôn ngữ không chỉ định tên tham số là một tùy chọn hợp lệ?

Tôi có thể nghĩ về ít nhất hai lý do:

  • Nói chung, giới thiệu một cú pháp thứ hai cho một nhu cầu đã được bảo hiểm (ở đây, truyền tham số) là một cái gì đó mà lợi ích của nó phải được cân bằng với chi phí vốn có của độ phức tạp ngôn ngữ được giới thiệu (cả trong việc thực hiện ngôn ngữ và mô hình tư duy của ngôn ngữ lập trình viên) ;
  • Nó làm cho việc thay đổi tên tham số trở thành một thay đổi phá vỡ API, có thể không phù hợp với kỳ vọng của nhà phát triển rằng việc thay đổi tên tham số là một thay đổi nhỏ, không phá vỡ;

Lưu ý rằng tôi không biết bất kỳ ngôn ngữ nào thực hiện các tham số được đặt tên mà không triển khai các tham số tùy chọn (yêu cầu tham số đã đặt tên) - vì vậy bạn nên cảnh giác khi đánh giá quá cao lợi ích dễ đọc của chúng, có thể đạt được một cách nhất quán hơn với định nghĩa về Giao diện lưu loát .


6
Tôi thường xuyên viết mã bằng nhiều ngôn ngữ khác nhau, tôi gần như luôn phải tìm kiếm các tham số cho một chuỗi con đơn giản trong bất kỳ ngôn ngữ nào. Nó có phải là chuỗi con (bắt đầu, độ dài) hay chuỗi con (bắt đầu, kết thúc) và là kết thúc được bao gồm trong chuỗi?
James Anderson

1
@JamesAnderson - Làm thế nào một chuỗi con tham số có tên sẽ giải quyết vấn đề của bạn? Bạn vẫn sẽ phải tra cứu các tham số để biết tên của tham số thứ hai là gì (trừ khi cả hai chức năng tồn tại và tính năng đa hình của ngôn ngữ cho phép các tham số cùng loại với các tên khác nhau).
mouviciel

6
@mouviciel: Các tham số được đặt tên không sử dụng nhiều cho người viết , nhưng thật tuyệt vời cho người đọc . Chúng ta đều biết các tên biến quan trọng như thế nào để tạo mã có thể đọc được và các tham số được đặt tên cho phép người tạo giao diện cung cấp tên tham số tốt cũng như tên hàm tốt, giúp mọi người sử dụng giao diện đó để viết mã dễ đọc hơn.
Phoshi

@Phoshi - Bạn nói đúng. Tôi chỉ quên tầm quan trọng của việc đọc mã khi tôi viết bình luận trước đó.
mouviciel

14

Các tham số được đặt tên làm cho mã dễ đọc hơn, khó viết hơn

Khi tôi đang đọc một đoạn mã, các tham số có tên có thể giới thiệu ngữ cảnh giúp mã dễ hiểu hơn. Xem xét ví dụ nhà xây dựng này : Color(1, 102, 205, 170). Điều đó có nghĩa là gì? Thật vậy, Color(alpha: 1, red: 102, green: 205, blue: 170)sẽ dễ đọc hơn nhiều. Nhưng than ôi, Trình biên dịch nói rằng không có ai - nó muốn Color(a: 1, r: 102, g: 205, b: 170). Khi viết mã bằng các tham số được đặt tên, bạn dành một lượng thời gian không cần thiết để tìm kiếm tên chính xác - việc quên tên chính xác của một số tham số sẽ dễ dàng hơn so với việc quên thứ tự của chúng.

Điều này một lần làm tôi đau đầu khi sử dụng DateTimeAPI có hai lớp anh chị em cho các điểm và thời lượng với giao diện gần như giống hệt nhau. Trong khi DateTime->new(...)chấp nhận một second => 30đối số, DateTime::Duration->new(...)mong muốn seconds => 30và tương tự cho các đơn vị khác. Vâng, nó hoàn toàn có ý nghĩa, nhưng điều này cho tôi thấy rằng các tham số được đặt tên ≠ dễ sử dụng.

Tên xấu thậm chí không làm cho nó dễ đọc hơn

Một ví dụ khác làm thế nào các tham số được đặt tên có thể xấu có lẽ là ngôn ngữ R. Đoạn mã này tạo ra một biểu đồ dữ liệu:

plot(plotdata$n, plotdata$mu, type="p", pch=17,  lty=1, bty="n", ann=FALSE, axes=FALSE)

Bạn thấy hai đối số vị trí cho các hàng dữ liệu xy và sau đó là danh sách các tham số được đặt tên. Có nhiều tùy chọn khác với mặc định và chỉ những tùy chọn được liệt kê có mặc định tôi muốn thay đổi hoặc chỉ định rõ ràng. Một khi chúng ta bỏ qua rằng mã này sử dụng các số ma thuật và có thể hưởng lợi từ việc sử dụng enums (nếu R có bất kỳ!), Vấn đề là nhiều tên tham số này không thể giải mã được.

  • pchthực sự là nhân vật cốt truyện, glyph sẽ được vẽ cho mọi điểm dữ liệu. 17là một vòng tròn trống rỗng, hoặc một cái gì đó như thế.
  • ltylà loại đường. Đây 1là một dòng rắn.
  • btylà loại hộp. Đặt nó để "n"tránh một hộp được vẽ xung quanh cốt truyện.
  • ann kiểm soát sự xuất hiện của các chú thích trục.

Đối với một người không biết ý nghĩa của mỗi từ viết tắt, các tùy chọn này khá khó hiểu. Điều này cũng tiết lộ lý do tại sao R sử dụng các nhãn này: Không phải là mã tự ghi, nhưng (là ngôn ngữ được gõ động) làm khóa để ánh xạ các giá trị tới các biến chính xác của chúng.

Thuộc tính của thông số và chữ ký

Chữ ký chức năng có thể có các thuộc tính sau:

  • Các đối số có thể được đặt hàng hoặc không có thứ tự,
  • được đặt tên hoặc không được đặt tên,
  • yêu cầu hoặc tùy chọn.
  • Chữ ký cũng có thể bị quá tải bởi kích thước hoặc loại,
  • và có thể có kích thước không xác định với varargs.

Các ngôn ngữ khác nhau hạ cánh tại các tọa độ khác nhau của hệ thống này. Trong C, các đối số được sắp xếp, không được đặt tên, luôn luôn được yêu cầu và có thể là các biến. Trong Java, tình huống tương tự, ngoại trừ chữ ký có thể bị quá tải. Trong Mục tiêu C, chữ ký được sắp xếp, đặt tên, bắt buộc và không thể bị quá tải vì đó chỉ là đường cú pháp xung quanh C.

Các ngôn ngữ được gõ động với varargs (giao diện dòng lệnh, Perl, Nhận) có thể mô phỏng các tham số được đặt tên tùy chọn. Các ngôn ngữ có quá tải kích thước chữ ký có một cái gì đó giống như các tham số tùy chọn vị trí.

Làm thế nào để không thực hiện các tham số được đặt tên

Khi nghĩ về các tham số được đặt tên, chúng ta thường giả sử các tham số có tên, tùy chọn, không có thứ tự. Thực hiện những điều này là khó khăn.

Các tham số tùy chọn có thể có các giá trị mặc định. Chúng phải được chỉ định bởi hàm được gọi và không nên được biên dịch thành mã gọi. Mặt khác, mặc định không thể được cập nhật mà không biên dịch lại tất cả các mã phụ thuộc.

Bây giờ một câu hỏi quan trọng là làm thế nào các đối số thực sự được truyền cho hàm. Với các tham số được sắp xếp, các đối số có thể được truyền vào một thanh ghi hoặc theo thứ tự vốn có của chúng trên ngăn xếp. Khi chúng tôi loại trừ các thanh ghi trong giây lát, vấn đề là làm thế nào để đưa các đối số tùy chọn không có thứ tự lên ngăn xếp.

Đối với điều đó, chúng ta cần một số thứ tự trên các đối số tùy chọn. Nếu mã khai báo bị thay đổi thì sao? Vì thứ tự không liên quan, việc sắp xếp lại trong khai báo hàm không nên thay đổi vị trí của các giá trị trên ngăn xếp. Chúng ta cũng nên xem xét nếu thêm một tham số tùy chọn mới là có thể. Từ góc độ người dùng, điều này có vẻ như vậy, bởi vì mã không sử dụng tham số đó trước đây vẫn sẽ hoạt động với tham số mới. Vì vậy, điều này không bao gồm các thứ tự như sử dụng thứ tự trong khai báo hoặc sử dụng thứ tự chữ cái.

Cũng xem xét điều này dưới ánh sáng của phân nhóm và Nguyên tắc thay thế Liskov - trong đầu ra được biên dịch, các hướng dẫn tương tự sẽ có thể gọi phương thức trên một kiểu con với các tham số có tên mới và trên siêu kiểu.

Triển khai có thể

Nếu chúng ta không thể có một thứ tự dứt khoát, vì vậy chúng ta cần một số cấu trúc dữ liệu không có thứ tự.

  • Việc thực hiện đơn giản nhất là chỉ cần truyền tên của các tham số cùng với các giá trị. Đây là cách các param được đặt tên được mô phỏng trong Perl hoặc với các công cụ dòng lệnh. Điều này giải quyết tất cả các vấn đề mở rộng được đề cập ở trên, nhưng có thể là một sự lãng phí không gian lớn - không phải là một tùy chọn trong mã quan trọng về hiệu năng. Ngoài ra, việc xử lý các thông số có tên này bây giờ phức tạp hơn nhiều so với việc đơn giản là bật các giá trị ra khỏi ngăn xếp.

    Trên thực tế, các yêu cầu không gian có thể được giảm bằng cách sử dụng gộp chuỗi, có thể giảm so sánh chuỗi sau với so sánh con trỏ (trừ khi không thể đảm bảo rằng chuỗi tĩnh thực sự được gộp lại, trong trường hợp đó, hai chuỗi sẽ phải được so sánh trong chi tiết).

  • Thay vào đó, chúng ta cũng có thể vượt qua một cấu trúc dữ liệu thông minh hoạt động như một từ điển của các đối số được đặt tên. Đây là giá rẻ ở phía người gọi, vì bộ phím được biết tĩnh tại vị trí cuộc gọi. Điều này sẽ cho phép tạo ra một hàm băm hoàn hảo hoặc để tính toán trước một trie. Callee vẫn sẽ phải kiểm tra sự tồn tại của tất cả các tên tham số có thể có phần đắt tiền. Một cái gì đó như thế này được sử dụng bởi Python.

Vì vậy, nó quá đắt trong hầu hết các trường hợp

Nếu một hàm với các tham số được đặt tên là có thể mở rộng đúng cách, thì có thể giả sử một thứ tự dứt khoát. Vì vậy, chỉ có hai giải pháp:

  • Đặt thứ tự của các thông số được đặt tên là một phần của chữ ký và không cho phép thay đổi sau này. Điều này hữu ích cho mã tự viết tài liệu, nhưng không giúp với các đối số tùy chọn.
  • Truyền cấu trúc dữ liệu khóa-giá trị cho callee, sau đó phải trích xuất thông tin hữu ích. Điều này rất tốn kém khi so sánh, và thường chỉ thấy trong các ngôn ngữ kịch bản mà không chú trọng đến hiệu suất.

Cạm bẫy khác

Các tên biến trong khai báo hàm thường có một số ý nghĩa bên trong và không phải là một phần của giao diện - ngay cả khi nhiều công cụ tài liệu vẫn sẽ hiển thị chúng. Trong nhiều trường hợp, bạn muốn các tên khác nhau cho một biến nội bộ và đối số được đặt tên tương ứng. Các ngôn ngữ không cho phép chọn tên hiển thị bên ngoài của tham số đã đặt tên sẽ không nhận được nhiều trong số chúng nếu tên biến không được sử dụng trong bối cảnh gọi.

Một vấn đề với các mô phỏng của các đối số được đặt tên là thiếu kiểm tra tĩnh ở phía người gọi. Điều này đặc biệt dễ quên khi chuyển từ điển đối số (nhìn vào bạn, Python). Điều này rất quan trọng vì việc chuyển từ điển là một cách giải quyết phổ biến, ví dụ như trong JavaScript : foo({bar: "baz", qux: 42}). Ở đây, không thể kiểm tra tĩnh các loại giá trị cũng như sự tồn tại hoặc vắng mặt của một số tên nhất định.

Mô phỏng các tham số được đặt tên (Trong các ngôn ngữ được nhập tĩnh)

Đơn giản chỉ cần sử dụng các chuỗi làm khóa và bất kỳ đối tượng nào làm giá trị đều không hữu ích khi có trình kiểm tra kiểu tĩnh. Tuy nhiên, các đối số được đặt tên có thể được mô phỏng bằng các cấu trúc hoặc nghĩa đen của đối tượng:

// Java

static abstract class Arguments {
  public String bar = "default";
  public int    qux = 0;
}

void foo(Arguments args) {
  ...
}

/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});

3
" it is easier to forget the exact names of some parameters than it is to forget their order" ... Đó là một khẳng định thú vị.
Catskul

1
Ví dụ phản biện đầu tiên không phải là vấn đề với các tham số được đặt tên và hơn nữa là một lỗ hổng với cách số nhiều tiếng Anh ánh xạ tới tên trường trong các giao diện. Ví dụ phản biện thứ hai cũng tương tự như một vấn đề với các nhà phát triển đưa ra các lựa chọn đặt tên xấu. (Không có sự lựa chọn nào về giao diện truyền tham số tự nó sẽ là điều kiện đủ để dễ đọc - câu hỏi đặt ra là liệu đó có phải là điều kiện cần thiết hay hỗ trợ cho nó trong một số trường hợp không).
mtraceur

1
+1 cho các điểm tổng thể về chi phí thực hiện và sự đánh đổi để thực hiện các tham số được đặt tên. Tôi chỉ nghĩ rằng bắt đầu sẽ mất người.
mtraceur

@mtraceur Bạn có một điểm. Bây giờ, 5 năm sau, có lẽ tôi sẽ viết câu trả lời hoàn toàn khác. Nếu bạn muốn, bạn có thể chỉnh sửa câu trả lời của tôi để loại bỏ những thứ ít hữu ích hơn. (Thông thường các chỉnh sửa như vậy sẽ bị từ chối vì chúng mâu thuẫn với ý định của tác giả, nhưng ở đây tôi ổn với nó). Ví dụ, bây giờ tôi tin rằng nhiều vấn đề về tham số được đặt tên chỉ là hạn chế của ngôn ngữ động và chúng chỉ nên có một hệ thống loại. Ví dụ: JavaScript có Flow và TypeScript, Python có MyPy. Với sự tích hợp IDE thích hợp giúp loại bỏ hầu hết các vấn đề về khả năng sử dụng. Tôi vẫn đang chờ đợi một cái gì đó ở Perl.
amon

7

Vì lý do tương tự mà Ký hiệu Hungary không còn được sử dụng rộng rãi; Intellisense (hoặc tương đương đạo đức của nó trong các IDE không phải của Microsoft). Hầu hết các IDE hiện đại sẽ cho bạn biết mọi thứ bạn cần biết về một tham số bằng cách chỉ di chuột qua tham chiếu tham số.

Điều đó nói rằng, một số ngôn ngữ hỗ trợ các tham số được đặt tên, bao gồm C # và Delphi. Trong C #, nó là tùy chọn, vì vậy bạn không phải sử dụng nó nếu bạn không muốn và có nhiều cách khác để khai báo cụ thể các thành viên, như khởi tạo đối tượng.

Các tham số được đặt tên chủ yếu hữu ích khi bạn chỉ muốn chỉ định một tập hợp con của các tham số tùy chọn, và không phải tất cả chúng. Trong C #, điều này rất hữu ích vì bạn không còn cần một loạt các phương thức quá tải để cung cấp cho lập trình viên tính linh hoạt này.


4
Python sử dụng các tham số được đặt tên và đó là một tính năng tuyệt vời của ngôn ngữ. Tôi muốn nhiều ngôn ngữ sử dụng nó. Nó làm cho các đối số mặc định dễ dàng hơn khi bạn không còn bị ép buộc, như trong C ++, để chỉ định rõ ràng bất kỳ đối số nào "trước" mặc định. (Như bạn đã có trong đoạn cuối của mình, đó là cách python thoát khỏi việc không bị quá tải ... đối số mặc định và đối số tên cho phép bạn thực hiện tất cả những điều tương tự.) Khi bạn có một cái gì đó như sin(radians=2), bạn không cần "Intellisense".
Gort Robot

2

Bash chắc chắn hỗ trợ phong cách lập trình này và cả Perl và Ruby đều hỗ trợ chuyển danh sách tham số tên / giá trị (như bất kỳ ngôn ngữ nào có hỗ trợ băm / bản đồ gốc). Không có gì ngăn bạn chọn định nghĩa cấu trúc / bản ghi hoặc hàm băm / bản đồ gắn tên với tham số.

Đối với các ngôn ngữ bao gồm băm / map / keyvalue lưu trữ nguyên bản, bạn có thể có được chức năng bạn muốn, chỉ bằng cách áp dụng thành ngữ để truyền băm khóa / giá trị cho các hàm / phương thức của bạn. Một khi bạn đã thử nó trong một dự án, bạn sẽ có cái nhìn sâu sắc hơn về việc bạn đã đạt được bất cứ điều gì, từ năng suất tăng có được từ việc dễ sử dụng, hoặc cải thiện chất lượng thông qua các khiếm khuyết giảm.

Lưu ý rằng các lập trình viên có kinh nghiệm đã trở nên thoải mái với việc truyền tham số theo thứ tự / vị trí. Bạn cũng có thể thấy rằng thành ngữ này chỉ có giá trị khi bạn có nhiều hơn một vài đối số (giả sử> 5/6). Và vì một số lượng lớn các đối số thường biểu thị cho các phương thức phức tạp (quá mức), bạn có thể thấy rằng thành ngữ này chỉ có lợi cho các phương thức phức tạp nhất.


2

Tôi nghĩ đó là lý do tương tự tại sao c # phổ biến hơn VB.net. Mặc dù VB.NET "dễ đọc" hơn, vd

Nó chỉ ra rằng những gì làm cho mã dễ hiểu hơn là sự đồng nhất. Càng ít càng tốt. Tên tham số hàm thường khá rõ ràng và không thực sự giúp bạn hiểu mã.


1
Tôi kịch liệt không đồng ý rằng "càng ít càng tốt". Được đưa đến cực kỳ logic, người chiến thắng golf mã sẽ là chương trình "tốt nhất".
Riley Major

2

Các tham số được đặt tên là một giải pháp cho một vấn đề trong việc tái cấu trúc mã nguồn và không nhằm mục đích làm cho mã nguồn nhiều hơn dễ đọc . Các tham số được đặt tên được sử dụng để hỗ trợ trình biên dịch / trình phân tích cú pháp giải quyết các giá trị mặc định cho các lệnh gọi hàm. Nếu bạn muốn làm cho mã dễ đọc hơn, hãy thêm các bình luận có ý nghĩa.

Các ngôn ngữ khó cấu trúc lại thường hỗ trợ các tham số được đặt tên, vì foo(1)sẽ bị phá vỡ khi chữ ký foo()thay đổi, nhưng foo(name:1)ít có khả năng phá vỡ đòi hỏi ít nỗ lực hơn từ người lập trình để thực hiện thay đổifoo .

Bạn làm gì khi bạn phải giới thiệu một tham số mới cho hàm sau và có hàng trăm hoặc hàng ngàn dòng mã gọi hàm đó?

foo(int weight, int height, int age = 0);

Hầu hết các lập trình viên sẽ làm như sau.

foo(int weight, int height, int age = 0, string gender = null);

Bây giờ, không cần tái cấu trúc và chức năng có thể thực thi trong chế độ cũ khi gender có giá trị.

Để cụ thể gendermột giá trị hiện được CỨNG vào cuộc gọi cho age. Như ví dụ này:

 foo(10,10,0,"female");

Các lập trình viên đã xem xét định nghĩa hàm, thấy rằng agecó giá trị mặc định là0 và sử dụng giá trị đó.

Bây giờ giá trị mặc định cho age trong định nghĩa hàm là hoàn toàn vô dụng.

Các tham số được đặt tên cho phép bạn tránh vấn đề này.

foo(weight: 10, height: 10, gender: "female");

Các tham số mới có thể được thêm vào foovà bạn không phải lo lắng về thứ tự chúng được thêm vào và bạn có thể thay đổi giá trị mặc định khi biết rằng mặc định bạn đặt thực sự là giá trị mặc định thực sự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.