Lợi ích của việc toán tử gán trả về một giá trị là gì?


27

Tôi đang phát triển một ngôn ngữ mà tôi dự định thay thế cả Javascript và PHP. (Tôi không thể thấy bất kỳ vấn đề nào với điều này. Nó không giống như một trong những ngôn ngữ này có cơ sở cài đặt lớn.)

Một trong những điều tôi muốn thay đổi là biến toán tử gán thành lệnh gán, loại bỏ khả năng sử dụng giá trị được trả về.

x=1;          /* Assignment. */
if (x==1) {}  /* Comparison. */
x==1;         /* Error or warning, I've not decided yet. */
if (x=1) {}   /* Error. */

Tôi biết rằng điều này có nghĩa là những chức năng một dòng mà những người C yêu thích rất nhiều sẽ không còn hoạt động. Tôi đã hình dung (với rất ít bằng chứng ngoài kinh nghiệm cá nhân của tôi) rằng phần lớn những lần này xảy ra, nó thực sự được dự định là hoạt động so sánh.

Hoặc là nó? Có bất kỳ việc sử dụng thực tế nào của giá trị trả về của toán tử gán không thể được viết lại một cách tầm thường không? (Đối với bất kỳ ngôn ngữ nào có khái niệm như vậy.)


12
JS và PHP không có "cơ sở cài đặt" lớn?
mhr

47
@mri Tôi nghi ngờ mỉa mai.
Andy Hunt

12
Trường hợp hữu ích duy nhất tôi có thể nhớ là while((x = getValue()) != null) {}. Thay thế sẽ xấu hơn vì bạn sẽ cần phải sử dụng breakhoặc lặp lại x = getValuebài tập.
CodeInChaos

12
@mri Ooh không, tôi nghe hai ngôn ngữ đó chỉ là những thứ tầm thường mà không có sự đầu tư đáng kể nào cả. Khi một vài người khăng khăng sử dụng JS thấy ngôn ngữ của tôi, họ sẽ chuyển sang ngôn ngữ của tôi và không bao giờ phải viết === nữa. Tôi cũng chắc chắn rằng các nhà sản xuất trình duyệt sẽ ngay lập tức tung ra một bản cập nhật bao gồm ngôn ngữ của tôi cùng với JS. :)
billpg

4
Tôi sẽ đề nghị với bạn rằng nếu ý định của bạn là nâng cao một ngôn ngữ hiện có và bạn dự định nó sẽ được áp dụng rộng rãi thì khả năng tương thích ngược 100% với ngôn ngữ hiện tại là tốt. Xem TypeScript như một ví dụ. Nếu ý định của bạn là cung cấp một sự thay thế tốt hơn cho một ngôn ngữ hiện có thì bạn có một vấn đề khó khăn hơn nhiều. Một ngôn ngữ mới phải giải quyết một vấn đề thực tế hiện có tốt hơn nhiều so với ngôn ngữ hiện có để trả chi phí chuyển đổi. Học một ngôn ngữ là một khoản đầu tư và nó cần phải trả hết.
Eric Lippert

Câu trả lời:


25

Về mặt kỹ thuật, một số đường cú pháp có thể đáng để giữ ngay cả khi nó có thể được thay thế một cách tầm thường, nếu nó cải thiện khả năng đọc của một số hoạt động phổ biến. Nhưng chuyển nhượng dưới dạng biểu thức không thuộc về điều đó. Sự nguy hiểm của lỗi đánh máy thay cho việc so sánh có nghĩa là nó hiếm khi được sử dụng (đôi khi thậm chí bị cấm bởi các hướng dẫn về phong cách) và gây ra một cú đúp bất cứ khi nào nó được sử dụng. Nói cách khác, lợi ích dễ đọc là nhỏ về số lượng và cường độ.

Một cái nhìn vào các ngôn ngữ hiện có làm điều này có thể đáng giá.

  • Java và C # tiếp tục gán một biểu thức nhưng loại bỏ cạm bẫy mà bạn đề cập bằng cách yêu cầu các điều kiện để đánh giá cho các booleans. Điều này chủ yếu có vẻ hoạt động tốt, mặc dù mọi người thỉnh thoảng phàn nàn rằng điều này không tuân theo các điều kiện như if (x)thay thế if (x != null)hoặc if (x != 0)tùy thuộc vào loại x.
  • Python thực hiện gán một câu lệnh thích hợp thay vì một biểu thức. Các đề xuất thay đổi điều này đôi khi đạt đến danh sách gửi thư của python, nhưng ấn tượng chủ quan của tôi là điều này hiếm khi xảy ra và tạo ra ít tiếng ồn hơn mỗi lần so với các tính năng "mất tích" khác, vòng lặp chuyển đổi, câu lệnh chuyển đổi, lambdas nhiều dòng, v.v.

Tuy nhiên, Python cho phép một trường hợp đặc biệt, gán cho nhiều tên cùng một lúc : a = b = c. Đây được coi là một tuyên bố tương đương b = c; a = bvà đôi khi nó được sử dụng, vì vậy nó cũng có thể đáng để thêm vào ngôn ngữ của bạn (nhưng tôi sẽ không tiết lộ nó, vì phần bổ sung này phải tương thích ngược).


5
+1 để đưa ra a = b = cnhững câu trả lời khác không thực sự đưa ra.
Leo

6
Nghị quyết thứ ba là sử dụng một biểu tượng khác để gán. Pascal sử dụng :=để gán.
Brian

5
@Brian: Thật vậy, cũng như C #. =là sự phân công, ==là sự so sánh.
Marjan Venema

3
Trong C #, một cái gì đó giống như if (a = true)sẽ đưa ra cảnh báo C4706 ( The test value in a conditional expression was the result of an assignment.). GCC với C cũng sẽ ném a warning: suggest parentheses around assignment used as truth value [-Wparentheses]. Những cảnh báo đó có thể được giữ im lặng với một bộ dấu ngoặc đơn bổ sung, nhưng chúng ở đó để khuyến khích một cách rõ ràng cho thấy sự phân công là có chủ ý.
Bob

2
@delnan Chỉ cần một lời nhận xét hơi chung chung, nhưng nó đã được gây ra bởi "loại bỏ các cạm bẫy bạn đề cập đến bằng cách yêu cầu điều kiện để đánh giá cho các phép toán luận" - a = true không đánh giá một Boolean và do đó không phải là một lỗi, nhưng nó cũng làm dấy lên một cảnh báo có liên quan trong C #.
Bob

11

Có bất kỳ việc sử dụng thực tế nào của giá trị trả về của toán tử gán không thể được viết lại một cách tầm thường không?

Nói chung là không. Ý tưởng có giá trị của biểu thức gán là giá trị được gán có nghĩa là chúng ta có một biểu thức có thể được sử dụng cho cả tác dụng phụgiá trị của nó và được nhiều người coi là khó hiểu.

Các cách sử dụng phổ biến thường làm cho biểu thức nhỏ gọn:

x = y = z;

có ngữ nghĩa trong C # của "convert z thành loại y, gán giá trị chuyển đổi cho y, giá trị được chuyển đổi là giá trị của biểu thức, chuyển đổi nó thành loại x, gán cho x".

Nhưng chúng ta đã ở trong vương quốc của các tác dụng phụ không hoàn hảo trong bối cảnh tuyên bố, vì vậy thực sự có rất ít lợi ích hấp dẫn cho điều đó hơn

y = z;
x = y;

Tương tự với

M(x = 123);

là một tốc ký cho

x = 123;
M(x);

Một lần nữa, trong mã gốc, chúng tôi đang sử dụng một biểu thức cho cả tác dụng phụ và giá trị của nó và chúng tôi đang đưa ra một tuyên bố có hai tác dụng phụ thay vì một. Cả hai đều có mùi; cố gắng có một tác dụng phụ cho mỗi câu lệnh và sử dụng các biểu thức cho các giá trị của chúng, không phải cho các tác dụng phụ của chúng.

Tôi đang phát triển một ngôn ngữ mà tôi dự định thay thế cả Javascript và PHP.

Nếu bạn thực sự muốn in đậm và nhấn mạnh rằng chuyển nhượng là một tuyên bố chứ không phải là một sự bình đẳng, thì lời khuyên của tôi là: làm cho nó rõ ràng là một tuyên bố chuyển nhượng .

let x be 1;

Thế là xong. Hoặc là

x <-- 1;

hoặc thậm chí tốt hơn:

1 --> x;

Hoặc thậm chí tốt hơn vẫn còn

1 → x;

Hoàn toàn không có cách nào mà bất kỳ ai trong số đó sẽ bị nhầm lẫn x == 1.


1
Thế giới đã sẵn sàng cho các ký hiệu Unicode không phải ASCII trong các ngôn ngữ lập trình chưa?
billpg

Tôi rất thích những gì bạn đề xuất, một trong những mục tiêu của tôi là hầu hết JavaScript "được viết tốt" có thể được chuyển qua với rất ít hoặc không cần sửa đổi.
billpg

2
@billpg: Thế giới đã sẵn sàng chưa? Tôi không biết - thế giới đã sẵn sàng cho APL vào năm 1964, nhiều thập kỷ trước khi phát minh ra Unicode? Đây là một chương trình trong APL chọn một hoán vị ngẫu nhiên gồm sáu số trong số 40 số đầu tiên: x[⍋x←6?40] APL yêu cầu bàn phím đặc biệt của riêng nó, nhưng đó là một ngôn ngữ khá thành công.
Eric Lippert

@billpg: Workshop của Macintosh Lập trình viên đã sử dụng các ký hiệu không phải ASCII cho những thứ như thẻ regex hoặc chuyển hướng của stderr. Mặt khác, MPW có lợi thế là Macintosh giúp dễ dàng nhập các ký tự không phải ASCII. Tôi phải thú nhận một số khó hiểu về lý do tại sao trình điều khiển bàn phím ở Hoa Kỳ không cung cấp bất kỳ phương tiện hợp lý nào để nhập bất kỳ ký tự không phải ASCII nào. Mục nhập số Alt không chỉ yêu cầu tra cứu mã ký tự - trong nhiều ứng dụng, nó thậm chí còn không hoạt động.
supercat

Hừm, tại sao người ta lại thích gán "bên phải" như thế a+b*c --> xnào? Điều này có vẻ lạ đối với tôi.
Ruslan

9

Nhiều ngôn ngữ chọn cách thực hiện gán một câu lệnh thay vì một biểu thức, bao gồm cả Python:

foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg

và Golang:

var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies

Các ngôn ngữ khác không có sự phân công, nhưng các ràng buộc trong phạm vi, ví dụ OCaml:

let foo = 42 in
  if foo = 42 then
    print_string "hi"

Tuy nhiên, letlà một biểu hiện chính nó.

Ưu điểm của việc cho phép gán là chúng ta có thể kiểm tra trực tiếp giá trị trả về của hàm bên trong điều kiện, ví dụ như trong đoạn Perl này:

if (my $result = some_computation()) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}

Perl cũng bổ sung phạm vi khai báo vào điều kiện duy nhất, điều này làm cho nó rất hữu ích. Nó cũng sẽ cảnh báo nếu bạn gán bên trong một điều kiện mà không khai báo một biến mới ở đó - if ($foo = $bar)sẽ cảnh báo, if (my $foo = $bar)sẽ không.

Việc chuyển nhượng trong một tuyên bố khác thường là đủ, nhưng có thể mang lại các vấn đề phạm vi:

my $result = some_computation()
if ($result) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}
# $result is still visible here - eek!

Golang phụ thuộc rất nhiều vào các giá trị trả về để kiểm tra lỗi. Do đó, nó cho phép một điều kiện để đưa ra một tuyên bố khởi tạo:

if result, err := some_computation(); err != nil {
  fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)

Các ngôn ngữ khác sử dụng một hệ thống loại để không cho phép các biểu thức không phải là boolean bên trong một điều kiện:

int foo;
if (foo = bar()) // Java does not like this

Tất nhiên điều đó không thành công khi sử dụng hàm trả về boolean.

Bây giờ chúng ta đã thấy các cơ chế khác nhau để bảo vệ chống lại sự phân công tình cờ:

  • Không cho phép chuyển nhượng như một biểu thức
  • Sử dụng kiểm tra kiểu tĩnh
  • Chuyển nhượng không tồn tại, chúng tôi chỉ có letràng buộc
  • Cho phép một câu lệnh khởi tạo, không cho phép chuyển nhượng
  • Không cho phép chuyển nhượng trong một điều kiện mà không cần khai báo

Tôi đã xếp chúng theo thứ tự ưu tiên tăng dần - các phép gán bên trong các biểu thức có thể hữu ích (và thật đơn giản để tránh các vấn đề của Python bằng cách sử dụng cú pháp khai báo rõ ràng và cú pháp đối số có tên khác). Nhưng không được phép không cho phép họ, vì có nhiều lựa chọn khác cho cùng một hiệu ứng.

Mã không có lỗi quan trọng hơn mã terse.


+1 cho "Không cho phép gán dưới dạng biểu thức". Các trường hợp sử dụng cho chuyển nhượng dưới dạng biểu thức không chứng minh tiềm năng cho các lỗi và các vấn đề dễ đọc.
chọc

7

Bạn nói "Tôi đã tìm ra (với rất ít bằng chứng ngoài kinh nghiệm cá nhân của tôi) rằng phần lớn những lần này xảy ra, nó thực sự có ý định là hoạt động so sánh."

Tại sao không CỐ ĐỊNH VẤN ĐỀ?

Thay vì = cho phép gán và == cho kiểm tra đẳng thức, tại sao không sử dụng: = cho phép gán và = (hoặc thậm chí ==) cho đẳng thức?

Quan sát:

if (a=foo(bar)) {}  // obviously equality
if (a := foo(bar)) { do something with a } // obviously assignment

Nếu bạn muốn làm cho lập trình viên khó hơn trong việc gán sai cho sự bình đẳng, thì hãy làm cho nó khó hơn.

Đồng thời, nếu bạn THỰC SỰ muốn khắc phục sự cố, bạn sẽ xóa Crock mà tuyên bố booleans chỉ là số nguyên với tên đường biểu tượng được xác định trước. Làm cho chúng một loại khác nhau hoàn toàn. Sau đó, thay vì nói

int a = some_value();
if (a) {}

bạn buộc người lập trình phải viết:

int a = some_value();
if (a /= 0) {} // Note that /= means 'not equal'.  This is your Ada lesson for today.

Thực tế là gán-as-an-toán tử là một cấu trúc rất hữu ích. Chúng tôi đã không loại bỏ lưỡi dao cạo vì một số người tự cắt. Thay vào đó, King Gillette đã phát minh ra dao cạo an toàn.


2
(1) :=cho sự phân công và =vì sự bình đẳng có thể khắc phục vấn đề này, nhưng với chi phí xa lánh mọi lập trình viên không lớn lên bằng cách sử dụng một nhóm nhỏ các ngôn ngữ không chính thống. (2) Các loại khác ngoài các bool được cho phép trong các điều kiện không phải lúc nào cũng do trộn lẫn các bool và các số nguyên, nó đủ để đưa ra một giải thích đúng / sai cho các loại khác. Ngôn ngữ mới hơn không sợ lệch khỏi C đã làm như vậy đối với nhiều loại khác với số nguyên (ví dụ: Python coi các bộ sưu tập trống là sai).

1
Và liên quan đến lưỡi dao cạo: Những người phục vụ một trường hợp sử dụng đòi hỏi sự sắc nét. Mặt khác, tôi không tin rằng lập trình tốt đòi hỏi phải gán cho các biến ở giữa một đánh giá biểu thức. Nếu có một cách đơn giản, công nghệ thấp, an toàn và tiết kiệm chi phí để làm cho lông trên cơ thể biến mất mà không có cạnh sắc, tôi chắc chắn rằng lưỡi dao cạo sẽ bị dịch chuyển hoặc ít nhất là hiếm hơn nhiều.

1
@delnan: Một người đàn ông khôn ngoan đã từng nói "Làm cho nó đơn giản nhất có thể, nhưng không đơn giản hơn." Nếu mục tiêu của bạn là loại bỏ phần lớn lỗi a = b so với a == b, việc hạn chế miền kiểm tra có điều kiện đối với booleans và loại bỏ các quy tắc chuyển đổi loại mặc định cho <other> -> boolean sẽ giúp bạn hoàn thành mọi cách ở đó Tại thời điểm đó, nếu (a = b) {} chỉ hợp pháp về mặt cú pháp nếu a và b đều là boolean và a là một giá trị pháp lý.
John R. Strohm

Việc gán một tuyên bố ít nhất là đơn giản như - thậm chí còn đơn giản hơn - những thay đổi bạn đề xuất và đạt được ít nhất - thậm chí còn nhiều hơn (thậm chí không cho phép xác if (a = b)định giá trị a, boolean a, b). Trong một ngôn ngữ không có kiểu gõ tĩnh, nó cũng cung cấp các thông báo lỗi tốt hơn nhiều (tại thời gian phân tích so với thời gian chạy). Ngoài ra, việc ngăn chặn "a = b so với a == b lỗi" có thể không phải là mục tiêu duy nhất có liên quan. Ví dụ, tôi cũng muốn cho phép mã if items:có ý nghĩa if len(items) != 0và tôi phải từ bỏ để hạn chế các điều kiện đối với booleans.

1
@delnan Pascal là ngôn ngữ không chính thống? Hàng triệu người đã học lập trình bằng Pascal (và / hoặc Modula, xuất phát từ Pascal). Và Delphi vẫn thường được sử dụng ở nhiều quốc gia (có thể không quá nhiều ở bạn).
jwenting

5

Để thực sự trả lời câu hỏi, vâng, có rất nhiều cách sử dụng này mặc dù chúng hơi thích hợp.

Ví dụ trong Java:

while ((Object ob = x.next()) != null) {
    // This will loop through calling next() until it returns null
    // The value of the returned object is available as ob within the loop
}

Việc thay thế mà không sử dụng phép gán nhúng yêu cầu obxác định bên ngoài phạm vi của vòng lặp và hai vị trí mã riêng biệt gọi x.next ().

Nó đã được đề cập rằng bạn có thể gán nhiều biến trong một bước.

x = y = z = 3;

Loại điều này là sử dụng phổ biến nhất, nhưng các lập trình viên sáng tạo sẽ luôn đưa ra nhiều hơn nữa.


Điều đó trong khi điều kiện vòng lặp sẽ giải quyết và tạo một obđối tượng mới với mỗi vòng lặp?
dùng3932000

@ user3932000 Trong trường hợp đó có lẽ là không, thường thì x.next () đang lặp lại một cái gì đó. Nó chắc chắn có thể là nó có thể mặc dù.
Tim B

1

Vì bạn có thể tạo nên tất cả các quy tắc, tại sao bây giờ cho phép gán để biến một giá trị và chỉ đơn giản là không cho phép các phép gán trong các bước có điều kiện? Điều này cung cấp cho bạn đường cú pháp để thực hiện khởi tạo dễ dàng, trong khi vẫn ngăn ngừa một lỗi mã hóa phổ biến.

Nói cách khác, làm cho điều này hợp pháp:

a=b=c=0;

Nhưng làm cho điều này bất hợp pháp:

if (a=b) ...

2
Đó dường như là một quy tắc khá đặc biệt. Việc gán một tuyên bố và mở rộng nó để cho phép a = b = ccó vẻ trực giao hơn và cũng dễ thực hiện hơn. Hai cách tiếp cận này không đồng ý về việc gán biểu thức ( a + (b = c)), nhưng bạn không đứng về phía chúng vì vậy tôi cho rằng chúng không quan trọng.

"Dễ thực hiện" không nên được xem xét nhiều. Bạn đang xác định giao diện người dùng - đặt nhu cầu của người dùng lên hàng đầu. Bạn chỉ cần tự hỏi liệu hành vi này có giúp ích hay cản trở người dùng hay không.
Bryan Oakley

nếu bạn không cho phép chuyển đổi ngầm định thành bool thì bạn không phải lo lắng về việc chuyển nhượng trong các điều kiện
ratchet freak

Dễ dàng thực hiện hơn chỉ là một trong những lý lẽ của tôi. Phần còn lại thì sao? Từ góc độ UI, tôi có thể thêm rằng thiết kế không liên tục của IMHO và các ngoại lệ đặc biệt thường gây trở ngại cho người dùng trong việc tìm kiếm và nội tâm hóa các quy tắc.

@ratchetfreak bạn vẫn có thể gặp vấn đề với việc gán các công cụ thực tế
jk.

0

Bằng âm thanh của nó, bạn đang trên con đường tạo ra một ngôn ngữ khá nghiêm ngặt.

Với ý nghĩ đó, buộc mọi người phải viết:

a=c;
b=c;

thay vì:

a=b=c;

có vẻ như là một cải tiến để ngăn chặn mọi người làm:

if (a=b) {

khi họ có ý định làm:

if (a==b) {

nhưng cuối cùng, loại lỗi này rất dễ phát hiện và cảnh báo về việc chúng có phải là mã hợp pháp hay không.

Tuy nhiên, có những tình huống làm:

a=c;
b=c;

Không có nghĩa là

if (a==b) {

sẽ đúng

Nếu c thực sự là một hàm c () thì nó có thể trả về các kết quả khác nhau mỗi lần nó được gọi. (nó cũng có thể đắt tiền về mặt tính toán ...)

Tương tự như vậy nếu c là một con trỏ tới phần cứng ánh xạ bộ nhớ, thì

a=*c;
b=*c;

cả hai đều có khả năng khác nhau, và cũng có thể có các hiệu ứng điện tử trên phần cứng trên mỗi lần đọc.

Có rất nhiều hoán vị khác với phần cứng mà bạn cần phải chính xác về địa chỉ bộ nhớ được đọc từ đâu, được viết và theo các ràng buộc thời gian cụ thể, trong đó thực hiện nhiều bài tập trên cùng một dòng là nhanh chóng, đơn giản và rõ ràng, không có rủi ro về thời gian giới thiệu các biến tạm thời


4
Tương đương với a = b = ckhông a = c; b = c, nó b = c; a = b. Điều này tránh sự trùng lặp của các tác dụng phụ và cũng giữ cho việc sửa đổi abtheo cùng một thứ tự. Ngoài ra, tất cả các đối số liên quan đến phần cứng này đều khá ngu ngốc: Hầu hết các ngôn ngữ không phải là ngôn ngữ hệ thống và không được thiết kế để giải quyết các vấn đề này cũng như không được sử dụng trong các tình huống xảy ra các sự cố này. Điều này gấp đôi đối với một ngôn ngữ cố gắng thay thế JavaScript và / hoặc PHP.

delnan, vấn đề không phải là những ví dụ giả định, chúng là. Vấn đề vẫn là họ chỉ ra các loại vị trí mà việc viết a = b = c là phổ biến, và trong trường hợp phần cứng, được coi là thông lệ tốt, như OP yêu cầu. Tôi chắc chắn họ sẽ có thể xem xét mức độ phù hợp của họ với môi trường dự kiến ​​của họ
Michael Shaw

Nhìn lại, vấn đề của tôi với câu trả lời này không phải chủ yếu là nó tập trung vào các trường hợp sử dụng lập trình hệ thống (mặc dù điều đó đủ tệ, theo cách viết của nó), nhưng nó dựa vào việc giả định viết lại không chính xác. Các ví dụ không phải là ví dụ về những nơi a=b=cphổ biến / hữu ích, chúng là những ví dụ về những nơi mà trật tự và số lượng tác dụng phụ phải được quan tâm. Điều đó là hoàn toàn độc lập. Viết lại phép gán chuỗi chính xác và cả hai biến thể đều đúng như nhau.

@delnan: Giá trị được chuyển đổi thành loại btrong một temp và được chuyển đổi thành loại atrong một temp khác. Thời gian tương đối khi các giá trị đó thực sự được lưu trữ là không xác định. Từ góc độ thiết kế ngôn ngữ, tôi nghĩ sẽ hợp lý khi yêu cầu tất cả các giá trị trong một câu lệnh nhiều phép có loại phù hợp và có thể yêu cầu không ai trong số chúng có thể biến động.
supercat

0

Lợi ích lớn nhất đối với suy nghĩ của tôi về việc chuyển nhượng là một biểu thức là nó cho phép ngữ pháp của bạn đơn giản hơn nếu một trong những mục tiêu của bạn là "mọi thứ đều là biểu thức" - đặc biệt là mục tiêu của LISP.

Python không có cái này; nó có biểu thức và câu lệnh, gán là một câu lệnh. Nhưng vì Python định nghĩa một lambdabiểu mẫu là một biểu thức được tham số hóa duy nhất , điều đó có nghĩa là bạn không thể gán các biến trong lambda. Điều này đôi khi không thuận tiện, nhưng không phải là vấn đề nghiêm trọng và đó là nhược điểm duy nhất trong kinh nghiệm của tôi khi chuyển nhượng là một tuyên bố trong Python.

Một cách để cho phép chuyển nhượng, hay đúng hơn là hiệu ứng của chuyển nhượng, là một biểu thức mà không đưa ra khả năng if(x=1)xảy ra tai nạn mà C có là sử dụng letcấu trúc giống như LISP , như (let ((x 2) (y 3)) (+ x y))ngôn ngữ của bạn có thể đánh giá là 5. Sử dụng letcách này không nhất thiết phải là sự phân công trong ngôn ngữ của bạn, nếu bạn xác định letlà tạo phạm vi từ vựng. Xác định theo cách đó, một letcấu trúc có thể được biên dịch giống như cách xây dựng và gọi một hàm đóng được lồng với các đối số.

Mặt khác, nếu bạn chỉ quan tâm đến if(x=1)vụ án, nhưng muốn gán là biểu thức như trong C, có thể chỉ cần chọn các mã thông báo khác nhau là đủ. Bài tập: x := 1hoặc x <- 1. So sánh : x == 1. Lỗi cú pháp : x = 1.


1
letkhác với sự phân công theo nhiều cách hơn là về mặt kỹ thuật giới thiệu một biến mới trong một phạm vi mới. Đối với người mới bắt đầu, nó không có tác dụng đối với mã bên ngoài letcơ thể và do đó yêu cầu lồng tất cả mã (nên sử dụng biến), một nhược điểm đáng kể trong mã nặng gán. Nếu một người đi xuống tuyến đường đó, set!sẽ là tương tự Lisp tốt hơn - hoàn toàn không giống như so sánh, nhưng không yêu cầu lồng hoặc một phạm vi mới.

@delnan: Tôi muốn thấy một cú pháp khai báo và gán kết hợp sẽ cấm phân công lại nhưng sẽ cho phép khai báo lại, theo các quy tắc rằng (1) khai báo sẽ chỉ hợp pháp đối với các định danh khai báo và gán và (2 ) khai báo lại sẽ "khai báo" một biến trong tất cả các phạm vi kèm theo. Do đó, giá trị của bất kỳ định danh hợp lệ nào sẽ là bất cứ thứ gì được gán trong khai báo trước đó của tên đó. Điều đó có vẻ đẹp hơn một chút so với việc phải thêm các khối phạm vi cho các biến chỉ được sử dụng cho một vài dòng hoặc phải tạo tên mới cho mỗi biến tạm thời.
supercat

0

Tôi biết rằng điều này có nghĩa là những chức năng một dòng mà những người C yêu thích rất nhiều sẽ không còn hoạt động. Tôi đã hình dung (với rất ít bằng chứng ngoài kinh nghiệm cá nhân của tôi) rằng phần lớn những lần này xảy ra, nó thực sự được dự định là hoạt động so sánh.

Thật. Điều này không có gì mới, tất cả các tập hợp con an toàn của ngôn ngữ C đã đưa ra kết luận này.

MISRA-C, CERT-C và như vậy trên tất cả các lệnh cấm trong điều kiện, đơn giản vì nó nguy hiểm.

Không tồn tại trường hợp mã dựa vào gán trong các điều kiện bên trong không thể được viết lại.


Hơn nữa, các tiêu chuẩn như vậy cũng cảnh báo chống lại việc viết mã dựa trên thứ tự đánh giá. Nhiều bài tập trên một hàng x=y=z;là một trường hợp như vậy. Nếu một hàng có nhiều bài tập chứa các tác dụng phụ (gọi hàm, truy cập các biến dễ bay hơi, v.v.), bạn không thể biết tác dụng phụ nào sẽ xảy ra trước.

Không có điểm thứ tự giữa việc đánh giá các toán hạng. Vì vậy, chúng tôi không thể biết liệu biểu hiện phụ yđược đánh giá trước hay sau z: đó là hành vi không xác định trong C. Do đó, mã đó có khả năng không đáng tin cậy, không di động và không tuân thủ các tập hợp con an toàn đã đề cập của C.

Giải pháp sẽ là thay thế mã bằng y=z; x=y;. Điều này thêm một điểm trình tự và đảm bảo thứ tự đánh giá.


Vì vậy, dựa trên tất cả các vấn đề gây ra trong C, bất kỳ ngôn ngữ hiện đại nào cũng sẽ làm tốt cả việc cấm chuyển nhượng trong các điều kiện, cũng như nhiều bài tập trên một hàng.

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.