2 điểm bởi GN⁺ 2024-01-15 | 1 bình luận | Chia sẻ qua WhatsApp

Suy nghĩ bằng ngôn ngữ K

  • Lập trình K chủ yếu được thực hiện thông qua REPL.
  • rlwrap của ngn/k cho phép duyệt lịch sử bằng các phím mũi tên, rất hữu ích khi phát triển các chương trình lớn.
  • Các hàm được thử nghiệm trong REPL rồi mới chuyển vào mã thực tế.
  • Cơ chế in đẹp của ngn/k luôn trả về dữ liệu K hợp lệ và có thể được tính toán trước để cải thiện tốc độ chương trình.
  • Script K được chạy như thể được nhập vào REPL, và giá trị trả về của mỗi dòng sẽ được in ra trừ khi dòng đó kết thúc bằng dấu chấm phẩy.
  • Script cho phép định nghĩa nhiều dòng, giúp tăng khả năng đọc.
  • Để lưu công việc vào script và dùng trong REPL, có thể dùng \\lfile.k để chạy tệp và nạp dữ liệu.
  • Có thể nạp cùng một tệp nhiều lần trong REPL để ghi đè dữ liệu trước đó.
  • Phần trợ giúp REPL truy cập bằng \\ có nhiều lệnh hữu ích khác nhau.

Đơn giản hóa lập trình mảng

  • Lập trình mảng là một quá trình liên tục nhằm đơn giản hóa các mẫu phức tạp thành các mẫu nhỏ hơn, mang tính khai báo hơn và dễ đọc hơn.
  • Cách đơn giản hóa các mẫu phức tạp được bàn luận chi tiết trong "Patterns and Antipatterns in APL: Escape the Beginner's Plateau - Aaron Hsu - Dyalog '17".

Chuyển phép nhân ma trận sang K

  • Có thể chuyển trực tiếp thuật toán lặp của phép nhân ma trận lấy từ bài viết trên Wikipedia sang K.
  • Ví dụ mã tệ nhất khi chuyển sang K cần rất nhiều phép gán biến toàn cục, vòng lặp lồng nhau và nhiều chỉnh sửa.
  • Có thể đơn giản hóa mã để giải quyết từng vấn đề như vậy một cách lần lượt.

Đơn giản hóa vòng lặp bên trong

  • Trong vòng lặp bên trong, có thể đơn giản hóa bằng cách dùng sum với fold (/).
  • ' (each) trả về một mảng, nên có thể loại bỏ biến toàn cục C.
  • Có thể loại bỏ các biến i, j, k để đơn giản hóa vòng lặp.

Loại bỏ vòng lặp và giảm thiểu biến toàn cục

  • Có thể loại bỏ vòng lặp trung gian bằng cách ghép trực tiếp hàng và cột mà không cần k.
  • Để loại bỏ j, có thể ghép từng cột của B với A[i].
  • Để loại bỏ i, có thể dùng eachleft để ghép từng hàng của A với từng cột của B.
  • Không còn cần đến biến toàn cục nữa.

Dạng cuối cùng của hàm nhân ma trận

  • + (chuyển vị) tốn chi phí, nên có thể loại bỏ.
  • Thay vì nhân từng hàng của x với từng cột của y, có thể thực hiện ngầm cùng công việc đó bằng cách áp từng hàng của B lên toàn bộ A.
  • Cuối cùng thu được một hàm nhân ma trận ngắn gọn và tường minh.
  • Quá trình đơn giản hóa mã ban đầu trải qua nhiều bước, nhưng sẽ trở nên dễ và trực quan hơn khi thành thạo K.
  • Nhân ma trận là một quy trình đơn giản rất phù hợp với khả năng hỗ trợ mảng của K.
  • Các chương sau sẽ xem xét thêm nhiều thuật toán ít phù hợp hơn với K và cách xử lý chúng.

Ý kiến của GN⁺

  • Bài viết này cho thấy cách dùng ngôn ngữ K để đơn giản hóa và tối ưu hóa các thuật toán như nhân ma trận.
  • Phản hồi tức thì qua REPL và việc cải tiến mã lặp đi lặp lại là những đặc trưng cốt lõi của lập trình K, đồng thời cũng là phương pháp học hữu ích ngay cả với kỹ sư phần mềm mới vào nghề.
  • Quá trình đơn giản hóa mã rất quan trọng để nâng cao năng lực lập trình, và bài viết này giải thích dễ hiểu quá trình đó thông qua các ví dụ cụ thể.

1 bình luận

 
GN⁺ 2024-01-15
Ý kiến trên Hacker News
  • Nhiều người đặt câu hỏi về tính hữu dụng và khả năng dễ hiểu của các ngôn ngữ mảng.

    • Ngôn ngữ mảng không phù hợp với mọi bài toán.
    • Nhưng lại cực kỳ hiệu quả một cách đáng ngạc nhiên với nhiều loại bài toán.
    • Người dùng ngôn ngữ mảng nhìn chung rất thông minh.
    • Học cách các ngôn ngữ mảng vận hành là một thử thách lớn.
    • Việc viết mã "thủ tục" trong ngôn ngữ mảng là một điều rất tệ.
    • Hiểu được lập trình ngầm định (tacit programming) là một trải nghiệm tuyệt vời, mở rộng tư duy.
    • Trải nghiệm khi nội tại hóa các chuỗi động từ (verb trains).
    • Hiểu cách các ngôn ngữ dựa trên mảng xử lý các mảng ở mọi số chiều.
    • Hiểu cách under hoạt động.
    • Hiểu cách hàm mũ của hàm (function exponents) hoạt động.
  • Có rất nhiều khía cạnh đáng kinh ngạc của ngôn ngữ mảng, và danh sách trên chỉ là một phần trong số đó.

    • Việc theo dõi Aaron Hsu phát triển một trình biên dịch APL song song đã khiến người viết tin vào tiềm năng thực sự của ngôn ngữ mảng.
    • Thảo luận về "mật độ ngữ nghĩa" (semantic density) của mã APL.
  • Nếu bạn chưa từng nghe về lập trình mảng và muốn có một phần giới thiệu, hãy xem "The Array Cast".

  • Có người đã khám phá APL/APL2 vào thập niên 70 và rất yêu thích nó, nhưng lại thấy khả năng cấu thành hàm còn hấp dẫn hơn.

    • Haskell thuần túy và có kiểu, nên thú vị và mạnh mẽ hơn APL.
    • "Ký pháp như một công cụ để tư duy" của APL có vẻ như là một lập luận để biện minh cho sự ngắn gọn quá mức.
  • Điều ngộ ra quan trọng nhất khi dùng ngôn ngữ mảng:

    • Động từ chính là thuật toán.
    • Một chuỗi động từ (hoặc trạng từ) là cách cấu thành trực tiếp nhất mà người viết từng dùng.
    • Chương trình là sự cấu thành của các thuật toán, chứ không phải tập hợp các câu lệnh và biểu thức.
    • Khái niệm xử lý nhất quán miền xác định và miền giá trị trên các mảng, map và hàm.
    • Cách đánh giá từ trái sang phải mà mắt không cần phải "nhảy qua nhảy lại" khi đọc mã.
    • Có thể và nên đưa mã tới dữ liệu.
    • Lợi thế bổ sung của ngôn ngữ K: có thể tự triển khai các view (tức là dependency), và có thể nạp mã nóng thông qua trình thông dịch.
  • Một câu hỏi về ngôn ngữ mảng: làm thế nào để thực hiện việc như "tìm mọi số nhỏ hơn N mà thỏa điều kiện P"?

    • Trong ngôn ngữ mảng, thông thường người ta tạo một mảng từ 1 đến N, kiểm tra điều kiện trên mảng đó, rồi áp dụng một mặt nạ để chỉ lấy các phần tử thỏa điều kiện.
    • Nếu N lớn và điều kiện hiếm khi đúng, việc tạo ra quá nhiều mảng tạm một cách không cần thiết có vẻ là lãng phí bộ nhớ và tài nguyên.
    • Các cách triển khai ngôn ngữ mảng có thể tối ưu hóa vấn đề này, hoặc giải quyết bằng các kỹ thuật như đánh giá lười (lazy evaluation).
  • Trải nghiệm với ngôn ngữ J: mô hình tư duy của ngôn ngữ mảng có phần thiên lệch, và không chắc việc nghĩ mọi bài toán như sự lồng nhau của các mảng có thực sự hữu ích hay không.

    • Việc tự do tạo ra các cấu trúc dữ liệu có thể đơn giản hóa bài toán sẽ giúp đơn giản hóa đáng kể phần thuật toán.
    • Để dùng APL/J/K, có vẻ bạn phải thông minh hơn vì kiểu thiên lệch này.
  • Ấn tượng khi giải các bài toán bằng ngôn ngữ K: K là một ngôn ngữ cố ý khó hiểu.

    • Đây là ngôn ngữ phù hợp với các câu đố và lời giải khéo léo, nhưng làm việc với mảng numpy trong Python cũng giúp học về ngôn ngữ mảng và cách tư duy bằng mảng.
  • Ví dụ trong ngôn ngữ mảng J: dùng dot =: +/ . * để tính tích vô hướng (dot product) của P và Q.

  • Cú pháp của K ngắn hơn, nhưng bạn phải giữ trong đầu rất nhiều ngữ cảnh ngầm định về cách ngôn ngữ K hoạt động.