Bạn có nên hy sinh khả năng đọc mã với mức độ hiệu quả của mã? [đóng cửa]


37

Bạn có nên hy sinh khả năng đọc mã với mức độ hiệu quả của mã?

ví dụ 3 dòng mã thành 1 dòng.

Tôi đọc trong Mã Craft bởi Pete Goodliffe rằng khả năng đọc là chìa khóa.

Suy nghĩ của bạn?


13
Tại sao bạn nghĩ mã với ít dòng có khả năng hiệu quả hơn? Điều đó hiếm khi xảy ra với các ngôn ngữ hiện đại, mặc dù nó có thể đã áp dụng cho các trình thông dịch BASIC 8 bit.
David Thornley

69
Không thể đọc và hiệu suất được đo bằng dòng.

Trong một vài trường hợp, tôi sẽ hy sinh khả năng đọc cho tốc độ, nhưng rất hiếm khi. Mã nhúng chạy máy móc tốc độ cao là một trường hợp. Đối với hầu hết các phần mềm, khả năng đọc là quan trọng hơn nhiều.
Jim C

1
Câu trả lời liên quan: lập trình
Nicole

Tôi sẽ luôn luôn ủng hộ khả năng đọc cho đến khi hiệu suất trở thành một vấn đề. Sau đó tôi sẽ bắt đầu lo lắng về nó.
Pete

Câu trả lời:


62

"Ít dòng hơn" không phải lúc nào cũng giống như "hiệu quả hơn". Tôi giả sử bạn có nghĩa là "Nên làm một chương trình ngắn hơn với chi phí dễ đọc".

Các chương trình phải được viết để mọi người đọc và chỉ tình cờ cho các máy thực thi.

-Eelson & Sussman, cấu trúc và giải thích các chương trình máy tính

Nói chung, tôi nghĩ điều quan trọng hơn là một chương trình dễ hiểu hơn là ngắn gọn. Mặc dù vậy, tôi cần lưu ý rằng việc làm cho chương trình ngắn hơn thường xuyên cũng giúp nó dễ đọc hơn (có ngưỡng rõ ràng mà bạn nhận được khi mã của bạn bắt đầu trông giống như nhiễu đường truyền, nhưng đến thời điểm đó, diễn đạt một cái gì đó ngắn gọn hơn dường như làm cho nó rõ ràng hơn).

Có các trường hợp ngoại lệ cụ thể (như tập lệnh shell cá nhân của bạn hoặc một trong các mã munging dữ liệu) mà không ai cần phải duy trì, và chỉ có bạn mới cần đọc. Trong tình huống đó, có thể chấp nhận hy sinh một số khả năng dễ đọc miễn là bạn vẫn có thể hiểu được.


3
+1 Có lợi nhuận giảm dần, nhưng tôi cũng thấy rằng một chương trình ngắn thường dễ đọc hơn chương trình dài.
Michael K

23
Thật không may, tôi đã tìm thấy mã chuyển đổi dữ liệu một lần mà không bị xóa ngay lập tức biến thành mã dài hạn, quá thường xuyên. Luôn mong đợi mọi thứ xung quanh, được sử dụng lại và mở rộng, trừ khi bạn xóa mã.
Vatine

1
Tôi đồng ý với Vatine. Bạn có bao giờ quay lại và cố gắng tìm ra thứ gì đó mà bạn từng nghĩ là "hoàn toàn rõ ràng" trở lại khi nào?
Wonko the Sane

+ 1 cho SICP. Cuốn sách tuyệt vời
Jason Yeo

30

Thỉnh thoảng đúng.

Khả năng đọc là một điều tốt để phấn đấu. Hầu hết các mã được viết cho các ứng dụng kinh doanh điển hình sẽ đủ hiệu quả và tập trung vào khả năng đọc là rất quan trọng. Trong các lĩnh vực đòi hỏi hiệu năng cao hơn (như lập trình trò chơi video hoặc tính toán nặng nề), điều quan trọng là phải từ bỏ khả năng đọc để sử dụng một tính năng ngôn ngữ cụ thể mà không thể đọc được và cực kỳ hiệu quả.

Để biết ví dụ về cái sau, hãy xem bài viết Fast Inverse Square Root trên Wikipedia.

Tôi thường nghĩ rằng tốt hơn là làm cho một cái gì đó có thể đọc được trước và lo lắng về hiệu suất sau đó, cung cấp các biện pháp phòng ngừa thông thường như không chọn thuật toán O (n ^ 2) so với O (n). Hy sinh khả năng đọc chỉ vì lợi ích của một mình, trong suy nghĩ của tôi, là sai lầm.


4
Tôi nghĩ rằng có thể có một sự khác biệt giữa "mã có thể đọc" và "biết thuật toán". Tôi đoán bất kỳ mã nào, nếu bạn không biết thuật toán, sẽ khó đọc và hiểu hơn. Ví dụ, trong trường hợp FISR được đề cập, mã đơn giản thực sự khá dễ đọc, nhưng thuật toán không được ghi lại. Nếu bạn biết thuật toán FISR, bạn có thể viết mã dễ đọc hơn bao nhiêu? Câu hỏi liên quan chặt chẽ có thể là: khi nào nên chọn một thuật toán ưa thích?
Maglob

@Maglob Tôi đặc biệt đề cập đến việc sử dụng 0x5f3759df. Không liên quan đến hiệu suất, bạn có thể sử dụng triển khai thuật toán ISR bằng cách sử dụng phép chia thông thường, điều này có thể dễ đọc hơn đối với một người ít quen thuộc với các phần bên trong máy tính.
Adam Lear

3
Điều này có lẽ có thể được thể hiện là "Đôi khi chính xác để thay thế một thuật toán ngây thơ được thể hiện trong 5 dòng nhận xét và 20 dòng mã bằng thuật toán tinh vi được thể hiện trong 15 dòng nhận xét và 5 dòng mã".
Peter Taylor

1
Cũng nên nhớ rằng những gì gây khó chịu khủng khiếp cho nhà phát triển trong một miền có thể là một thành ngữ hoàn toàn chấp nhận được đối với nhà phát triển ở một miền khác. Mặc dù hằng số ma thuật trong thuật toán ISR dường như đã đẩy các giới hạn lên một chút, tôi đoán rằng tại thời điểm đó, việc hack cấp độ bit cho các xấp xỉ điểm nổi là khá phổ biến trong phát triển trò chơi vào thời điểm đó. Tương tự như vậy, làm việc trong các hệ thống nhúng có rất nhiều điều khó hiểu là thành ngữ nhưng có vẻ quá khó hiểu đối với một nhà phát triển ứng dụng.
Cercerilla

một trong những điều kỳ diệu của Internet -> khi thực hiện một thuật toán phức tạp (ví dụ: khoảng cách levenshtein với tối ưu hóa đường chéo ... Tôi chỉ đang làm việc trên nó;)), người ta có thể tham khảo một bài viết hoặc thậm chí sao chép bài viết trong tài liệu của dự án và đưa một tài liệu tham khảo vào mã. Bằng cách này, mọi người biết các thuật toán chỉ cần làm theo các nhận xét giải thích các thử nghiệm / tối ưu hóa cụ thể, trong khi những người mới bắt đầu trước tiên sẽ phải đọc về thuật toán và sau đó quay lại thực hiện.
Matthieu M.

22

Lần duy nhất tôi hy sinh khả năng đọc là khi mã được hiển thị là nút cổ chai hiệu năng và việc viết lại sẽ khắc phục điều đó. Trong trường hợp đó, ý định của mã phải được ghi lại rõ ràng để nếu có lỗi, nó có thể được theo dõi dễ dàng hơn.

Điều đó không nói rằng việc viết lại phải không thể đọc được.


22

Tôi đã trích dẫn nó trước đây và tôi sẽ trích dẫn lại:

Làm cho nó chính xác,
làm cho nó rõ ràng,
làm cho nó súc tích,
làm cho nó nhanh chóng.

Theo thứ tự đó.

Wes Dyer


2
+1 Tôi đã cuộn xuống nhanh chóng để đảm bảo báo giá này được đưa vào. Bây giờ tôi không phải đưa ra câu trả lời. :-)
RationalGeek

9

Bạn có nên hy sinh khả năng đọc mã với mức độ hiệu quả của mã?

Về mặt mã, nó luôn luôn tốt đẹp để tự ghi lại tài liệu. Nhưng đôi khi điều đó không thể xảy ra. Đôi khi bạn cần phải tối ưu hóa và đôi khi bản thân mã đó không dễ đọc.

Đây là những gì bình luận đã được phát minh cho . Sử dụng chúng. Ngay cả lắp ráp cũng có ý kiến. Nếu bạn đã viết một khối mã và không có bình luận nào trước mắt, tôi lo lắng. Nhận xét không ảnh hưởng đến hiệu suất thời gian chạy, nhưng một số lưu ý về những gì đang diễn ra luôn có ích.

Trong suy nghĩ của tôi, hoàn toàn không có lý do gì để không có một vài bình luận cơ bản. Rõ ràng if ( x == 0 ) /* check if x is 0 */là hoàn toàn vô dụng; bạn không nên thêm tiếng ồn không cần thiết vào mã, nhưng đại loại như thế này:

; this is a fast implementation of gcd
; in C, call as int gcd(int a, int b)
; args go in rdi, rsi, rcx, r8, r9
gcd:
    push rdp
    ; ...

Là khá hữu ích.


6

Bạn có nên hy sinh khả năng đọc mã với mức độ hiệu quả của mã?

Nếu hiệu quả là mục tiêu hiện tại của bạn (như trong giai đoạn tối ưu hóa) và bạn biết - bạn có số liệu, phải không? - dòng mã đó là nút cổ chai hiện tại, sau đó có.

Mặt khác, không: khả năng đọc sẽ cho phép bạn (hoặc một số khác) có thể thay đổi mã này sau để làm cho nó hiệu quả hơn, vì nó dễ hiểu hơn.


4

Không ai thắng Code Golf

ví dụ 3 dòng mã thành 1 dòng

Một ý tưởng đặc biệt khủng khiếp.

Chi phí để chơi golf mã - rất cao.

Chi phí để duy trì các chương trình không thể đọc được - thiên văn.

Giá trị của loại mã tối thiểu này - không. Nó vẫn hoạt động, nhưng không hoạt động "tốt hơn".

Hiệu quả khôn ngoan

Chi phí để chọn đúng thuật toán và cấu trúc dữ liệu - vừa phải.

Chi phí để duy trì thuật toán và cấu trúc dữ liệu phù hợp - thấp.

Giá trị của thuật toán và cấu trúc dữ liệu phù hợp - cao. Sử dụng tài nguyên thấp.

Foolish ("tối ưu hóa vi mô") Hiệu quả

Chi phí để chơi xung quanh tối ưu hóa vi mô - cao.

Chi phí để duy trì mã không thể đọc được, tối ưu hóa vi mô - rất cao.

Giá trị của tối ưu hóa vi mô - khác nhau. Khi có giá trị khác không ở đây, chi phí vẫn lớn hơn nó.


2

Phụ thuộc vào việc chúng ta đang nói về hiệu quả về tốc độ thực thi mã hay hiệu quả trong việc nhà phát triển có thể viết mã nhanh như thế nào. Nếu bạn đang hy sinh tính dễ đọc của mã để có thể nhập mã rất nhanh thì có thể bạn sẽ thấy mình phải trả lại thời gian để gỡ lỗi.

Tuy nhiên, nếu chúng ta đang nói về việc hy sinh khả năng đọc mã về tốc độ mã thực thi thì có khả năng là một sự đánh đổi chấp nhận được miễn là mã phải được tạo ra một cách hiệu quả. Viết một cái gì đó chạy càng nhanh càng tốt chỉ vì bạn không thể có lý do gần như vì nó là một cái gì đó giống như căn bậc hai nghịch đảo nhanh trong đó hiệu suất là chìa khóa. Bí quyết sẽ là giữa việc cân bằng mã và đảm bảo rằng mặc dù nguồn có thể khó đọc, các ý kiến ​​mô tả những gì nó giải thích những gì đang diễn ra.



2

Tôi không chấp nhận đối số "dễ đọc hơn hiệu suất". Hãy để tôi cung cấp cho bạn một câu trả lời với một spin khác nhau trên nó.

Một số nền tảng: Bạn biết những gì làm cho tôi bị bệnh? Khi tôi nhấp đúp chuột vào Máy tính của tôi và tôi thực sự phải đợi nó xuất hiện. Nếu điều đó mất nhiều hơn 5 giây, tôi thực sự thất vọng. Điều ngu ngốc là, và đừng đổ lỗi cho Microsoft vì điều này, là trong một số trường hợp, lý do khiến nó mất quá nhiều thời gian là một quyết định cần phải được đưa ra cho biểu tượng nào sẽ hiển thị! Đúng rồi. Vì vậy, ở đây tôi đang ngồi, chỉ quan tâm đến việc đi vào ổ C: của tôi và tôi phải đợi trình điều khiển truy cập vào đĩa CD-ROM của mình và đọc biểu tượng từ đó (giả sử có ổ đĩa CD trong ổ đĩa).

ĐƯỢC. Vì vậy, chỉ trong một giây, hãy tưởng tượng tất cả các lớp giữa tôi nhấp đúp vào Máy tính của tôi và nó thực sự đang nói chuyện thông qua trình điều khiển với CD-ROM. Bây giờ hãy tưởng tượng mọi lớp đều ... nhanh hơn ...

Bạn thấy đấy, đằng sau tất cả những điều này là 1000 lập trình viên hạnh phúc vì mã của họ "dễ đọc hơn". Thật tuyệt. Tôi mừng cho bạn. Nhưng từ quan điểm của người dùng, nó chỉ hút (thuật ngữ kỹ thuật). Và vì vậy, bạn ngủ ngon vào ban đêm nói với bản thân rằng bạn đã làm đúng bằng cách đảm bảo mã dễ đọc hơn và chậm hơn. Thậm chí chậm hơn một chút so với nó có thể. Và vì vậy, 1000 nhà phát triển làm điều này và cuối cùng chúng tôi chờ đợi PC của chúng tôi vì bạn. Theo tôi bạn không xứng đáng. Tôi không nói rằng những dòng đầu tiên của bạn cần phải là tốt nhất.

Đây là cách tiếp cận của tôi: Đầu tiên, làm cho nó hoạt động, sau đó làm cho nó nhanh hơn. Luôn luôn hướng tới việc viết mã hiệu quả và nếu bạn phải hy sinh khả năng đọc, hãy bổ sung nó bằng các bình luận. Tôi sẽ không hy sinh hiệu quả để một số lập trình viên tầm thường có thể duy trì nó. Tuy nhiên tôi sẽ giải thích mã của tôi, nhưng nếu điều đó là không đủ, tôi xin lỗi, bạn chỉ đơn giản là không đủ năng lực để làm việc ở đây. Bởi vì ở đây, chúng tôi viết mã nhanh và dễ đọc, và mặc dù có sự cân bằng, mã có thể đọc được có thể được giải thích trong khi không hiệu quả là không thể chấp nhận được.


"OK. Vì vậy, chỉ trong một giây, hãy tưởng tượng tất cả các lớp giữa tôi nhấp đúp vào Máy tính của tôi và nó thực sự nói chuyện thông qua trình điều khiển với CD-ROM. Bây giờ hãy tưởng tượng mọi lớp đều ... nhanh hơn ..." ngay lập tức với tôi với 2 DVD Ổ đĩa
Rangoric

Một từ: Nâng cấp
jmort253

2
Các bạn, làm việc với tôi ở đây, đó là một minh họa ...
Maltrap

@Rangoric đó là những gì tôi gọi bằng cách sử dụng những tiến bộ trong công nghệ như một cái nạng chứ không phải là một món quà. Nó hoạt động tốt cho ngành công nghiệp nếu bạn có thể thuyết phục người dùng mở ví của họ thường xuyên hơn.
Jonathan Neufeld

Tôi nghĩ rằng tác động năng lượng của mã cồng kềnh đòi hỏi sự xem xét kỹ lưỡng và nghiêm ngặt hơn. Quản lý môi trường đang thiếu ở đây. Xem xét rằng chúng ta đang nhìn vào sự gia tăng 4 độ trong nhiệt độ toàn cầu, tại sao sự phức tạp tính toán lại chiếm chỗ dựa?
Jonathan Neufeld

2

Câu hỏi này thường đi vào tâm trí của tôi khi các cuộc phỏng vấn được thảo luận trong văn phòng. Nhiều năm trước khi tốt nghiệp, tôi đã được hỏi câu hỏi "Bạn có nghĩ rằng mã là tài liệu tự?". Bây giờ, tôi đã trả lời câu hỏi này với tư cách là một lập trình viên và theo như người phỏng vấn quan tâm, đó là một câu hỏi trắng đen, vì vậy không có trung gian. Quá trình này sẽ tồn tại lâu hơn so với mọi người vì mọi người sẽ sống động hơn và bạn muốn bắt đầu mới càng sớm càng tốt, và mã càng dễ đọc, càng nhanh để hiểu những gì đang diễn ra.

Tôi đã đọc một cuốn sách trước đây khá hay, được gọi là Phát triển theo hướng tên miền : Thiết kế theo hướng tên miền: Xử lý sự phức tạp trong trái tim của phần mềm Phải thừa nhận rằng lúc đầu nó hơi khô khan, nhưng tài liệu được trình bày tốt. Điều này cho thấy một sự chấp thuận tốt dẫn đến các hệ thống tài liệu tốt. Ngôn ngữ là phương tiện để truyền đạt giải pháp của bạn, vì vậy giải pháp càng được thể hiện rõ ràng, càng dễ thích nghi nếu hiệu suất thực sự trở thành một yếu tố quan trọng. Đó là niềm tin của tôi và nó dường như đã làm việc tốt với tôi.


1

Hiếm khi ROI về việc làm cho mã chạy nhanh hơn với chi phí dễ đọc là xứng đáng. Các máy tính hiện đại chạy quá nhanh đến nỗi tôi nghi ngờ sẽ có một kịch bản mà bạn muốn điều này. Nếu một máy tính đang chạy mã, mã đó cần được duy trì.

Cuối cùng, tôi thấy khả năng đọc rất quan trọng. Tất nhiên, như đã nói nhiều lần, chỉ vì mã có thể đọc được không cần thiết có nghĩa là nó chậm hơn.

Một ví dụ điển hình là một tên biến: $a

Là gì $a?? Điều này nằm ngoài ngữ cảnh nên bạn không thể trả lời nhưng bạn đã bao giờ gặp phải vấn đề này trong mã thực tế chưa? Bây giờ giả sử ai đó đã viết $file_handle- bây giờ đó là gì? Nó rõ ràng ngay cả trong bối cảnh. Độ dài của tên biến tạo ra sự khác biệt không đáng kể cho máy tính.

Tôi nghĩ rằng có lẽ thường có ở đây.

Một số ứng dụng có thể đảm bảo cắt ngắn bit mà không phải tất cả sẽ hiểu nhưng tôi nghĩ rằng tại một thời điểm nào đó có lợi nhuận giảm dần và việc tìm kiếm một kịch bản là hiếm *.

* điều này không phụ thuộc vào ngành công nghiệp và những thứ khác. Tôi đang xem xét điều này từ quan điểm của nhà phát triển phần mềm kinh doanh (Hệ thống thông tin doanh nghiệp).


Để xem xét điều này từ một quan điểm khác (nhưng không lan man), tôi làm việc tại một công ty SAAS. Khi một trang web bị sập, chúng tôi phải sửa nó thật sự, rất nhanh - thường thì có người khác đang sửa mã của nhà phát triển khác.

Tôi muốn nhiều hơn một người nào đó làm điều gì đó rất không hiệu quả nhưng có thể đọc hơn để làm cho nó lạ mắt và "nhanh". Các máy chủ web của chúng tôi rất tiên tiến và một yêu cầu không cần phải được gửi trong một phần triệu giây. Chúng tôi không có vấn đề tải.

Vì vậy, trong thực tế tôi nghĩ rằng bạn có nhiều khả năng làm tổn thương chính mình hoặc người khác ... (tôi muốn có ngày cuối tuần của tôi trở lại.)


1

Đối với hầu hết mọi trường hợp, câu trả lời là "Tin tưởng trình biên dịch của bạn thực hiện công việc của nó" và viết mã có thể đọc được. Điều này ngụ ý rằng mã được cấu trúc logic (nghĩa là không có spaghetti) và tự viết tài liệu (nghĩa là đủ tên rõ ràng của các biến, hàm, v.v.). Mã bổ sung không tự ghi lại với các ý kiến ​​có ý nghĩa. Đừng bình luận vì mục đích bình luận, tức là

x++; // Add one to x

Thay vào đó, bình luận cho bạn, người đọc, trong 6 tháng hoặc 12 tháng hoặc một số thời gian đủ dài khác. Áp dụng một tiêu chuẩn mã hóa, và làm theo nó.


+1 cho "Tin tưởng trình biên dịch của bạn để thực hiện công việc". Đó là bình luận Skype mới của tôi.
jmort253

0

Mã sạch là mã nhanh. Viết rõ ràng, dễ duy trì mã có xu hướng nhanh hơn bởi vì nó là một chỉ báo cho thấy người lập trình hiểu nhiệm vụ trong tay và tái cấu trúc mã theo mục đích cốt lõi của nó.

Bên cạnh đó, trình biên dịch hiện đại tối ưu hóa hướng dẫn rất hiệu quả. Có bao nhiêu dòng mã bạn nhập để làm một cái gì đó và những gì trình biên dịch tạo ra theo hướng dẫn không nhất thiết phải liên quan. Đọc trên trình biên dịch để hiểu tại sao đó là trường hợp.

Khi tôi đang làm việc với một thứ gì đó dựa trên hiệu năng như đồ họa, đôi khi tôi sẽ hy sinh khả năng đọc / bảo trì khi tôi làm những việc như xử lý hình ảnh khi tôi làm việc với các thuật toán lồng nhau sâu nhất trong đó tối ưu hóa nhỏ có thể có ảnh hưởng lớn. Và thậm chí sau đó tôi chỉ làm như vậy sau khi định hình để đảm bảo những thay đổi thực sự tăng tốc mọi thứ. Tôi không thể nói cho bạn biết bao nhiêu lần tôi đã thử mã hóa 'tối ưu hóa' chỉ để thấy rằng nó thực sự làm chậm ứng dụng do cách trình biên dịch tối ưu hóa mã gõ tay.


-1

Khả năng đọc là một cái cớ cho các lập trình viên không đủ năng lực và lười biếng (thực tế cũng áp dụng cho "tính đơn giản" khi được sử dụng như một đối số để bảo vệ một thuật toán / thiết kế tào lao)!

Đối với mỗi vấn đề nhất định, bạn nên cố gắng cho giải pháp tối ưu! Thực tế là máy tính ngày nay nhanh không phải là lý do để lãng phí chu kỳ CPU. Hạn chế duy nhất là "thời gian giao hàng". Lưu ý rằng "giải pháp tối ưu" ở đây có nghĩa là giải pháp BẠN có thể đưa ra (tất cả chúng ta không thể đưa ra giải pháp tốt nhất; hoặc có kỹ năng / kiến ​​thức để thực hiện chúng).

Như một người khác đã đề cập nếu có những khía cạnh "khó hiểu" của một giải pháp, đó là những gì bình luận dành cho. Thứ tự "chính xác, dễ đọc, nhanh chóng" (hoặc đại loại như thế), mà người khác đề cập chỉ là lãng phí thời gian.

Tôi có một thời gian thực sự khó tin rằng có những lập trình viên ngoài kia, khi gặp vấn đề họ nghĩ trong dòng "... điều này phải được thực hiện như thế này nhưng tôi sẽ làm theo cách đó kém hiệu quả hơn nhưng dễ đọc hơn / có thể bảo trì và những thứ nhảm nhí khác ... ". Sai lầm của điều này là, nhà phát triển tiếp theo (nhìn thấy sự thiếu hiệu quả) rất có thể sẽ sửa đổi mã và tiếp theo sẽ làm như vậy và ... Kết quả cuối cùng là sau một vài phiên bản, mã sẽ trở thành bản gốc nhà phát triển nên đã viết ở vị trí số 1. Lý do duy nhất cho nhà phát triển ban đầu là a. Anh ấy / cô ấy đã không nghĩ về nó (đủ công bằng) và (như đã đề cập trước đó) b. hạn chế về thời gian và nguồn lực.


-2

nếu việc giảm khả năng đọc giúp hiệu suất / tối ưu hóa mã (như trong swfobject và các thư viện js khác) thì đó là lý do để tiếp tục viết mã được định dạng rõ ràng và dễ đọc và chuyển đổi thành không thể đọc / tối ưu hóa như một phần của quá trình "biên dịch" / phát hành.

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.