Bạn có thể học lập trình chức năng trong C? [đóng cửa]


9

Kết quả của cuộc thảo luận bình luận ở đây , tôi tự hỏi liệu bạn có thể học Lập trình hàm trong C không?


30
Bất kể bạn có thể hay không thể, bạn không nên .
R. Martinho Fernandes

27
Chắc chắn rồi! Bước đầu tiên là viết một trình thông dịch Lisp ;-)
Ferruccio

Nếu tôi hiểu đúng, câu hỏi đáng lẽ là về việc học các khái niệm khác nhau với mã giả, không có ngôn ngữ thực sự nào cả.
SK-logic

@ SK-logic: Nói một cách nhẹ nhàng, rất ít lập trình viên đã học lập trình hoàn toàn bằng mã giả, hầu hết phải làm bẩn tay và khuôn mặt của họ bị đập với thông báo lỗi từ trình biên dịch / trình thông dịch.
sbi

4
@sbi, một số lập trình viên giỏi nhất đã học lập trình khi trình biên dịch không có thông báo lỗi nào cả. Viết mã trên thẻ đục lỗ đòi hỏi một chút hiểu biết về những gì bạn đang làm từ lâu trước khi bạn có cơ hội chạy mã. Và không cần phải đề cập rằng cơ sở cho lập trình chức năng đã được thiết lập từ lâu trước khi một máy tính đầu tiên được chế tạo.
SK-logic

Câu trả lời:


21

Rõ ràng bạn có thể thực hiện lập trình chức năng trong C. Về lý thuyết, bạn cũng có thể học các nguyên tắc lập trình chức năng trong C, nhưng ngôn ngữ không làm cho nó dễ dàng.

Tôi giả sử bạn có ít nhất một chút nền tảng trong OOP; nếu bạn làm như vậy, bạn nên lưu ý rằng OOP có thể được thực hiện bằng C, bao gồm đa hình, getters / setters, quy tắc hiển thị, v.v., nhưng điều đó khá đau đớn để làm như vậy, và bạn cần biết cả OOP và C bên trong- ra để kéo nó ra Nó rất giống với FP.

Điều bạn nên làm trước tiên là học một ngôn ngữ lập trình chức năng (hầu hết chúng có các quy tắc cú pháp đơn giản đáng ngạc nhiên; đó không phải là cú pháp khiến chúng khó học), và sau đó để cho trí tuệ mới có được của bạn ảnh hưởng đến cách bạn viết C.


Theo yêu cầu, một vài điều bạn có thể học từ FP và sau đó áp dụng, giả sử, C, C ++ hoặc Java:

  • Tránh trạng thái, đặc biệt là trạng thái đột biến được chia sẻ
  • Đánh giá cao các chức năng thuần túy
  • Đánh giá cao sự lười biếng
  • Có thể mô hình về các động từ hơn là danh từ
  • Có thể tiếp cận các vấn đề đệ quy cũng như lặp đi lặp lại
  • Sử dụng các hàm bậc cao hơn
  • Suy nghĩ về các chức năng như một loại giá trị khác, mà bạn có thể vượt qua và kết hợp với các giá trị khác
  • Sử dụng các hàm hạng nhất như là một thay thế cho đa hình dựa trên đối tượng

1
Bạn có thể cung cấp các ví dụ cụ thể về cách các lập trình viên C / C ++ / Java có thể hưởng lợi từ sự khôn ngoan của LISP không. Nó có ý nghĩa trong lý thuyết, nhưng tôi đang tìm kiếm một cái gì đó cụ thể.
Công việc

@Job, lợi ích cụ thể nào có thể có? Nó mở rộng tâm trí của họ và có thể khiến họ nghĩ về trạng thái đột biến quá mức là một điều xấu, bạn còn đòi hỏi gì hơn nữa?
dan_waterworth

Vì vậy, tôi có thể kết luận từ điều này rằng, trong khi có thể học (một số) FP trong C, không có nghĩa gì để làm điều đó?
sbi

@Job: Tôi đã học FP khi tôi học LISP khi còn là sinh viên nhiều năm trước. Khoảng một thập kỷ sau, tôi đã áp dụng FP trong lập trình meta mẫu.
sbi

1
@sbi, cách yêu thích của tôi để học các ngôn ngữ và khái niệm mới là thông qua việc thực hiện chúng. Và C là một ngôn ngữ khá tốt để thực hiện một trình biên dịch. Vì vậy, vâng, bạn có thể học FP với C.
SK-logic

11

C có thể bị hack để cung cấp một số khái niệm chức năng:

Đây câu hỏi StackOverflow sẽ cho bạn biết thêm. Nhưng mặc dù có vẻ như có thể thực hiện lập trình chức năng (hoặc một tập hợp con lớn) trong C, hack và phần mở rộng trình biên dịch và bất cứ điều gì không phải là cách tốt nhất để tìm hiểu về một khái niệm.

Để thực sự học lập trình chức năng, đặt cược tốt nhất của bạn là một trong những ngôn ngữ lập trình chức năng nổi bật như Lisp và các phương ngữ của nó ( Clojure , Scheme ), ErlangHaskell . Bất kỳ một trong số đó là những công cụ hoàn hảo hoạt động trong tư duy lập trình chức năng. F # cũng là một ứng cử viên tốt nếu bạn có nền tảng .Net, nhưng đó là ngôn ngữ đa mô hình, không hoàn toàn là ngôn ngữ lập trình chức năng.


Như tdammers ghi chú trong các ý kiến:

Trên thực tế, LISP, clojure và sơ đồ cũng là đa mô hình; Haskell, trong khi thuần túy và lười biếng mặc định, cũng cho phép lập trình bắt buộc trong khi trong một bối cảnh đơn điệu, và nó có hỗ trợ rộng rãi để xử lý đồng thời. Tất cả những điều này có các cơ chế thực hiện phần lớn trí tuệ được tập hợp trong thế giới OOP - đóng gói, kế thừa, chịu trách nhiệm đơn lẻ, sáng tác, v.v. đó là về mô hình hình thành điểm bắt đầu của ngôn ngữ.

Theo hiểu biết tốt nhất của tôi, Lisp và phương ngữ của nó và Erlang là những ứng cử viên tốt hơn F # bởi vì họ khuyến khích lập trình chức năng hơn các mô hình khác, điều mà các nhà soạn thảo nói rõ là điểm khởi đầu của ngôn ngữ . F # không bao gồm lập trình chức năng nhưng không khuyến khích nó vượt qua các mô hình được hỗ trợ khác, lập trình bắt buộc và oo.


Về câu cuối cùng của bạn: Trong trường hợp đó, tôi cho rằng F # là một ứng cử viên tốt hơn bởi vì nó sẽ không nhốt bạn trong một tư duy chức năng. Trong một ngôn ngữ đa mô hình, khi bạn bắt đầu vặn ốc vít, bạn có thể chỉ cần chuyển sang một tuốc nơ vít. Khi bị mắc kẹt trong một mô hình duy nhất, bạn có thể bỏ lỡ sự khác biệt giữa ốc vít và đinh.
R. Martinho Fernandes

Câu hỏi là làm thế nào để học lập trình chức năng, không phải lập trình. Tôi giả định rằng op có một số kinh nghiệm trong ngôn ngữ đa mô hình và muốn cách hiệu quả nhất để học lập trình chức năng, trong trường hợp đó F # là một ứng cử viên tốt nhưng không phải là một ứng dụng hoàn hảo. Bị mắc kẹt trong một mô hình duy nhất rõ ràng là cách tốt nhất khi bạn đang muốn tìm hiểu về mô hình duy nhất đó vì bạn không phải đối phó với mọi thứ không phải như vậy.
yannis

Tôi tin rằng học khi mô hình phù hợp và khi không phù hợp với một nhiệm vụ nhất định là một phần của việc học nó, có lẽ là quan trọng nhất.
R. Martinho Fernandes

4
Trên thực tế, LISP, clojure và sơ đồ cũng là đa mô hình; Haskell, trong khi thuần túy và lười biếng mặc định, cũng cho phép lập trình bắt buộc trong khi trong một bối cảnh đơn điệu, và nó có hỗ trợ rộng rãi để xử lý đồng thời. Tất cả những điều này có các cơ chế thực hiện phần lớn trí tuệ được tập hợp trong thế giới OOP - đóng gói, kế thừa, chịu trách nhiệm đơn lẻ, sáng tác, v.v. đó là về mô hình hình thành điểm bắt đầu của ngôn ngữ.
tdammers

2
@MartinhoFernandes: Người ta có thể lập luận rằng bị mắc kẹt trong một mô hình duy nhất là cách hoàn hảo để học khi nó thực sự là một nỗi đau ở mông :-)
Jörg W Mittag

2

Bạn không thể học tất cả các khía cạnh của lập trình chức năng trong C. Nhưng chắc chắn bạn có thể bắt đầu lập trình kiểu chức năng với bất kỳ ngôn ngữ bắt buộc nào. Các bit khởi đầu này là - "Cách giữ mọi thứ thuần túy trong khi lập trình." Và nó cũng có thể được thực hiện. Kiểm tra bài đăng trên blog này để biết chi tiết-

http://www.johndcook.com/blog/2011/07/24/get-started-feftal-programming/


2

TL; DR

Lập trình chức năng là về đóng cửa và các ứng dụng của họ. Trừ khi ai đó có thể chỉ cho bạn một thư viện đóng cửa gốc cho C, hãy quên sử dụng C để học lập trình chức năng.

Lập trình chức năng là gì?

Khái niệm chính yếu của lập trình chức năng là khái niệm về các bao đóng , nói một cách đại khái, nắm bắt một chức năng cùng với các ràng buộc biến. Bên cạnh việc sử dụng phổ biến các bao đóng, có một vài đặc điểm khác biệt trong lập trình chức năng, như sử dụng các hàm đệ quy và các giá trị bất biến (cả hai đều chơi tốt với nhau). Những đặc điểm này là một vấn đề văn hóa hơn bất kỳ điều gì khác và không có sự cản trở kỹ thuật nào để sử dụng chúng trong hầu hết mọi ngôn ngữ, đây là lý do tại sao tôi tập trung vào việc đóng cửa trong câu trả lời của mình: không phải ngôn ngữ nào cũng cho phép dễ dàng tạo ra sự đóng cửa.

Ba minh họa về đóng cửa hữu ích

Một cách sử dụng điển hình của việc đóng cửa là việc thực hiện các cơ chế bảo mật. Ví dụ, mã Javascript - trong các ví dụ tôi đã chọn Javascript vì đây là ngôn ngữ chức năng với cú pháp được gọi là cú pháp giống C của C và câu hỏi của bạn cho thấy bạn đã quen thuộc với C:

create_counter = function()
{
  var x = 0;
  var counter = function()
  {
    ++x;
    return x;
  };
  return counter;
}

Sau đó với

a = create_counter();
b = create_counter();

chúng tôi có hai chức năng abđếm các bộ sưu tập rời rạc. Điểm của ví dụ là các biến xđược bắt bởi bao đóng xác định bao counterđóng và mỗi lần counterđóng is instantiated by the function, it gets its fresh own idea of whatx` mới là.

Một cách sử dụng điển hình khác của bao đóng là định nghĩa ứng dụng một phần của các hàm. Giả sử rằng chúng ta có một cơ sở báo cáo tương tự như việc syslogthực hiện một chức năng

var log = function(priority, message) {

};

trong đó các đối số prioritymessageđược dự kiến ​​là các chuỗi, đầu tiên là một trong số "debug", "info"v.v. Chúng ta có thể định nghĩa một nhà máy đăng nhập như thế này:

var logWithPriority = function(priority) {
  return function(message) {
    log(priority, message);
  };
};

và sử dụng nó để xác định các phiên bản chuyên biệt của cơ sở nhật ký của chúng tôi:

var debug = logWithPriority("debug");
var info = logWithPriority("info");

Điều này rất hữu ích, vì thay vì viết các lỗi dễ bị forlỗi như thế này

for(i = 0; i < journal.length; ++i) {
   log("info", journal[i]);
}

chúng ta có thể viết sạch hơn, ngắn hơn và đơn giản hơn nhiều (không có i, điều đó tốt hơn nhiều ):

journal.forEach(logWithPriority("info"));

Một lĩnh vực ứng dụng quan trọng thứ ba của việc đóng cửa là việc thực hiện đánh giá lười biếng - lưu ý rằng hỗ trợ ngôn ngữ đặc biệt có thể cung cấp cho việc triển khai tốt hơn.

Một hàm lười biếng, thay vì thực hiện một phép tính thẳng, trả về một bao đóng có thể được gọi (hoặc bị ép buộc trong một thuật ngữ của sự lười biếng) để thực hiện câu hỏi. Động lực để làm điều này là nó tách ra chuẩn bị một tính toán và thực hiện một tính toán. Một ví dụ thực tế về điều này là biên dịch biểu thức chính quy: nếu một chương trình biên dịch nhiều biểu thức chính quy khi khởi động, nó sẽ cần rất nhiều thời gian để bắt đầu. Nếu thay vào đó, chúng ta lười biếng biên dịch các biểu thức chính quy và buộc chúng khi chúng cần, thì chương trình của chúng ta có thể nhanh chóng bắt đầu. Tất nhiên, các biểu thức chính quy có thể được thay thế ở đây với bất kỳ cấu trúc nào đòi hỏi thời gian khởi tạo đáng kể.

Dưới đây là cách thực hiện đánh giá lười biếng với đóng cửa. Hãy xem xét việc triển khai cổ điển của hàm ArrayMax trả về max trong một mảng:

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  };
}

Biến thể lười biếng sẽ là:

function arrayMax(array) {
  var memo = null;
  function actuallyCompute() {
    if(memo === null) {
      memo = array.reduce(function(a, b) {
        return Math.min(a, b);
      });
    }
    return memo;
  }
  return actuallyCompute;
}

Giá trị được trả về là một bao đóng có thể được sử dụng để tính giá trị hoặc truy xuất nó vào lần khác nếu nó đã được tính toán.

Với ba ví dụ này, chúng ta nên tự tin rằng các bao đóng và các ứng dụng của chúng là cốt lõi của lập trình chức năng.

Phần kết luận

Học lập trình chức năng có nghĩa là học cách lập trình với các bao đóng. Do đó, các ngôn ngữ cho phép dễ dàng thao tác các bao đóng, và đặc biệt là ứng dụng một phần các chức năng, cần được xem xét khi tìm kiếm một ngôn ngữ để nghiên cứu lập trình chức năng. Ngược lại, các ngôn ngữ nơi đóng cửa không thể dễ dàng thao tác sẽ là lựa chọn kém.


1
Cảm ơn mẫu tốt đẹp này. BTW, như bạn định nghĩa thông tin var, nó sẽ không phải là tạp chí.forEach (thông tin)?
ejaenv

Vâng, nó sẽ là một biến thể thích hợp! :)
Michael Le Barbier Grünewald

1

Tôi nghĩ rằng các công cụ bạn sử dụng ảnh hưởng đến việc học của bạn rất nhiều. Hầu như không thể học các khái niệm lập trình mà ngôn ngữ lập trình bạn sử dụng không cung cấp phương tiện để sử dụng. Chắc chắn, bạn luôn có thể học một vài điều, nhưng bạn không thể học nó đúng cách.

Nhưng dù sao đó cũng là học thuật, bởi vì, như Martinho nói trong nhận xét của mình , ngay cả khi bạn có thể học lập trình chức năng, bạn không nên cố gắng làm điều đó, bởi vì có những ngôn ngữ mà việc này dễ hơn nhiều .


1

Bạn không nên học lập trình chức năng trong C, nhưng bằng ngôn ngữ chức năng nghiêm ngặt (Haskell, Caml, Erlang, v.v.).

Nếu bạn chưa quen với chức năng, bạn sẽ không bao giờ thực sự có được nó với một ngôn ngữ không chức năng. Nhiều khả năng, bạn sẽ tự rèn luyện để làm những gì bạn nghĩ là lập trình chức năng và học mọi thứ sai cách. Và luôn luôn khó khăn hơn để «học lại» mọi thứ theo cách đúng hơn là học chúng đúng cách lúc đầu.

Dù sao, tôi nghĩ làm chức năng trong C là một bài tập tốt cho những người đã biết chức năng. Bởi vì người đó sẽ học được những gì đang diễn ra đằng sau mui xe - những gì máy tính đang thực sự làm.

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.