Là sử dụng chức năng lồng nhau gọi là một điều xấu?


9

Trong một bài tập về nhà gần đây, tôi đã kết thúc việc gọi các chức năng của mình theo cách xấu xí uglyReceipt(cashParser(cashInput())), bản thân chương trình hoạt động hoàn hảo nhưng tôi vẫn cảm thấy như mình đang làm gì đó sai.

Là gọi các chức năng như thực hành xấu này và nếu vậy: Tôi nên làm gì thay thế?



1
Là những hàm lồng nhau, hay đó là một tổ của các lệnh gọi hàm? Nếu OP sau có thể đã (tái) phát minh ra lập trình chức năng.
Dấu hiệu suất cao

Điều gì làm cho bạn nghĩ rằng đây sẽ là một thực hành xấu?
Ixrec

1
@gnat: Câu hỏi đó hoàn toàn không liên quan. Câu hỏi đó là về các định nghĩa hàm được lồng trong từ vựng, câu hỏi này là về việc chuyển kết quả của một lệnh gọi hàm làm đối số cho một lệnh gọi hàm khác.
Jörg W Mittag

Tôi đã đọc sai - cảm ơn vì đã chỉ ra @ JörgWMittag (rút lại phiếu bầu)
gnat

Câu trả lời:


11

Điều này thực sự phụ thuộc vào số lượng lồng bạn sử dụng. Rốt cuộc, bạn được phép sử dụng kết quả chức năng trực tiếp trong các biểu thức để cải thiện khả năng đọc. Cả hai, mã không sử dụng các biểu thức lồng nhau (như mã trình biên dịch mã) và mã sử dụng quá nhiều biểu thức lồng nhau rất khó đọc. Mã tốt cố gắng đạt được sự cân bằng ở giữa các thái cực.

Vì vậy, hãy nhìn vào một số ví dụ. Một trong những bạn đưa ra trong câu hỏi của bạn có vẻ khá hợp pháp với tôi, vì vậy không có gì phải lo lắng ở đây. Tuy nhiên, một dòng như

foo(bar(baz(moo, fab), bim(bam(ext, rel, woot, baz(moo, fab)), noob), bom, zak(bif)));

chắc chắn sẽ không thể chịu đựng được Tương tự như vậy, mã như

double xsquare = x*x;
double ysquare = y*y;
double zsquare = z*z;
double xysquare = xsquare + ysquare;
double xyzsquare = xysquare + zsquare;
double length = sqrt(xyzsquare);

sẽ không được đọc rất tốt là tốt. sqrt(x*x + y*y + z*z)dễ hiểu hơn nhiều, mặc dù nó kết hợp tổng cộng sáu thao tác khác nhau trong một biểu thức.

Lời khuyên của tôi là hãy chú ý đến những biểu hiện mà bạn vẫn có thể phân tích trong đầu một cách dễ dàng. Thời điểm bạn cần có một cái nhìn thứ hai để nắm bắt một biểu thức duy nhất làm gì, đã đến lúc giới thiệu một biến bổ sung.


4

Tôi nghĩ rằng nó tốt hay xấu phụ thuộc rất nhiều vào bối cảnh. Lý do chính có thể bị coi là xấu là vì nó (được cho là) ​​làm cho mã khó đọc và gỡ lỗi hơn. Điều này đặc biệt đúng khi bạn lần đầu tiên học lập trình. Khi các kỹ năng mã hóa (và mã) của bạn được nâng cao hơn, có những lúc điều này được chấp nhận.

Ví dụ: xem xét một thông báo lỗi như thế này:

line 1492: missing argument "foo"

Làm thế nào để bạn biết nếu đối số bỏ lỡ là cashInput, cashParserhoặc uglyReceipt? Tùy thuộc vào ngôn ngữ, thông báo lỗi có thể cho bạn biết, nhưng có thể không.

Nếu muốn tách các cuộc gọi chức năng đó ra và thông báo lỗi vẫn chỉ cho bạn đến dòng 1492, bạn sẽ biết ngay vấn đề nằm ở đâu:

1491: input = cashInput()
1492: parsed_value = cashParser(input)
1493: receipt = uglyReceipt(parsed_value)

Với các bước được tách ra một cách riêng biệt, việc gỡ lỗi sẽ dễ dàng hơn nhiều vì có thể đặt điểm dừng ở bất kỳ bước nào và bạn có thể dễ dàng chèn giá trị bằng cách thay đổi giá trị của các biến cục bộ.


3

Khái niệm bên dưới câu hỏi của bạn rất quan trọng Tôi cảm thấy nó cần một câu trả lời khác thay vì chỉ là một nhận xét (như tôi đã bắt đầu làm).

3 câu trả lời còn lại cho đến nay cung cấp một số điểm xem xét hữu ích về việc liệu một tình huống cụ thể có xứng đáng bằng cách sử dụng cái mà bạn gọi là "các hàm gọi lồng nhau" hay không. Nhưng có lẽ một điểm quan trọng hơn được ẩn giấu trong các bình luận dưới câu hỏi của bạn: trong trường hợp bạn bỏ lỡ sự tinh tế trong những gì mà những người thông thái đang gợi ý, Carl, bạn đã tự mình khám phá ra chủ đề thực sự gọi là lập trình chức năng . Nếu bạn chưa bao giờ thấy thuật ngữ này, bạn có thể không nghĩ đó thực sự là một "điều" trong bình luận của @ HighPerformanceMark.

Nhưng thực sự nó là như vậy! Lập trình chức năng đã được viết trong nhiều thập kỷ, kể từ bài báo bán nguyệt của John Hughes Tại sao lại là vấn đề lập trình chức năng . Có một số ngôn ngữ là ngôn ngữ chức năng (nghĩa là chúng chỉ cho phép bạn viết theo kiểu lập trình chức năng), các ngôn ngữ như Erlang, Lisp, OCaml hoặc Haskell. Nhưng có nhiều ngôn ngữ khác là ngôn ngữ bắt buộc / chức năng lai. Đó là, chúng là các ngôn ngữ bắt buộc truyền thống nhưng cũng cung cấp một số hỗ trợ cho lập trình chức năng, bao gồm Perl, C ++, Java, C # và nhiều ngôn ngữ khác. Mục nhập của Wikipedia về lập trình chức năng cung cấp một phần hay cho thấy sự so sánh giữa phong cách chức năng và phong cách bắt buộc đối với một số ngôn ngữ.

Có nhiều điều để nói về sự khác biệt giữa phong cách bắt buộc và chức năng, nhưng điểm khởi đầu quan trọng là với lập trình chức năng, các hàm hoặc phương thức không có tác dụng phụ , nói chung dễ hiểu hơn về cả chương trình gỡ lỗi và gỡ lỗi.

Để đọc thêm, bạn cũng có thể xem qua Tại sao "Tại sao lại có vấn đề về lập trình chức năng" của Reginald Braithwaite và một bài viết thú vị khác ở đây về SO, Tại sao lại là ngôn ngữ chức năng?


2

Nó hoàn toàn không phải là một thực hành xấu nói chung. Hàm gọi các giá trị chấp nhận và một cách tạo ra giá trị là bằng cách gọi hàm khác.

Khi tôi thấy một biến được định nghĩa, như:

parsed_value = cashParser(input)

... Tôi phải xem xét rằng parsed_valuecó thể được sử dụng nhiều lần và có lẽ tôi sẽ phải kiểm tra xem điều này có đúng hay không (nếu thay đổi của tôi phá vỡ điều gì đó ở nơi khác?). Khi bạn bắt đầu thêm nhiều biến, nó có thể trở nên phức tạp hơn đối với người đọc để theo dõi tất cả chúng và cách chúng có liên quan. Vì vậy, tôi cảm thấy nhẹ nhõm khi tôi thấy:

receipt = uglyReceipt(cashParser(input))

... bởi vì phạm vi / thời gian tồn tại của giá trị trung gian là hiển nhiên. Bây giờ, như mọi khi, việc tách một biểu thức dài thành các câu lệnh riêng biệt có thể giúp ích, đặc biệt nếu một tên biến có thể cho độ chính xác cao hơn về mục đích của một giá trị:

user_name = input()
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.