2013-01-04 29 views
6

Tôi muốn thực hiện các hàm bậc cao hơn (HOF) trong C làm đường cú pháp với nỗ lực tối thiểu. Ví dụ, đối với đoạn mã sauCác hàm bậc cao hơn trong C dưới dạng đường cú pháp với nỗ lực tối thiểu

function add(int x) { 
    return int(int y) { 
    return x + y; 
    }; 
} 

int main() { 
    function add1 = add(1); 
    return add1(2); 
} 

nó được transcompiled vào C tinh khiết như

#include <stdlib.h> 

typedef struct { 
    void *ctx; 
    void* (*fun)(void *arg, void *ctx); 
} function; 

function new_function(void *ctx, void* (*fun)(void *, void *)) { 
    function f = {.ctx=ctx, .fun=fun}; 
    return f; 
} 

void* apply(function f, void *arg) { 
    return (*(f.fun))(arg, f.ctx); 
} 

typedef struct { 
    int x; 
} context$1; 

void* new_context$1(int x) { 
    context$1 *ctx = malloc(sizeof(context$1)); 
    ctx->x = x; 
    return ctx; 
} 

void* function$1(void *arg, void *ctx) { 
    int y = (int)arg; 
    int x = ((context$1*)ctx)->x; 
    return (void*)(x + y); 
} 

function add(int x) { 
    return new_function(new_context$1(x), function$1); 
} 

int main() { 
    function add1 = add(1); 
    return (int)apply(add1, (void*)2); 
} 

Tôi đã chạy phiên bản (bằng tay) transcompiled và nó hoạt động tốt. Để thực hiện, tôi tin rằng một số thao tác AST và nâng lambda là đủ.

Có bất kỳ sai sót tiềm năng nào trong cách tiếp cận của tôi không? Có phương pháp nào dễ dàng hơn cho HOF hay tôi có thể cải thiện phương pháp tiếp cận của mình để dễ thực hiện hơn không?

Trả lời

2

Có hai vấn đề rõ ràng bây giờ:

  • Sử dụng void * để đại diện cho tham số và trả về giá trị của bất kỳ loại cuối cùng sẽ phá vỡ. Xem xét các tham số "dài" trên ia-32 hoặc bất kỳ cấu trúc nào được truyền theo giá trị.
  • Thật khó (nếu có thể) để thực hiện các chức năng bậc cao hữu ích mà không cần thu gom rác thải. Bạn có thể nhìn thấy nó từ ví dụ của riêng bạn, nơi bối cảnh $ 1 là malloc'ed nhưng không bao giờ free'd. Boehm GC có thể trợ giúp ở đây.
+0

Tôi hoàn toàn đồng ý với điểm thứ hai của bạn và +1 cho Boehm GC :-). Nhưng có thể vui lòng giáo dục tôi như thế nào sẽ void * cuối cùng phá vỡ?Tôi tin rằng họ sẽ làm việc tốt vì chúng được tạo tự động. –

+0

@XiaoJia: vấn đề với 'void *' nằm trong 'hàm $ 1'. Điều gì sẽ xảy ra nếu loại 'x + y' lớn hơn' void * '? Trong ví dụ này có nghĩa là 'sizeof (int)> sizeof (void *)', đó sẽ là một thực thi C lạ, nhưng không quá lạ nếu 'x' có kiểu' long long' thay vì 'int'. Sau đó, các diễn viên để 'void *' mất thông tin. –

+0

Vâng, bạn luôn có thể nói rằng trình dịch tự động của bạn sẽ chăm sóc nó bằng cách nào đó (miễn là bạn không mô tả thuật toán nhưng chỉ cung cấp một bản dịch ví dụ). Nhưng tôi chỉ nhìn vào (void *) 2 và thấy rằng nó sẽ mất thông tin nếu có một số giá trị lớn hơn con trỏ thay vì 2. (Và truyền đến/từ void * sẽ không hoạt động ở tất cả các cấu trúc được truyền theo giá trị; bạn có từ chối hỗ trợ tất cả hay không, hoặc bạn sẽ dịch các chức năng như vậy một cách khác nhau?) –

1

Lưu ý rằng mã được tạo của bạn sẽ gọi hành vi không xác định. Ở một số nơi, bạn đang chuyển đổi giữa các loại tích phân và kiểu con trỏ qua truyền trực tiếp. Điều này không hợp pháp trong C. Bạn không đảm bảo rằng một con trỏ và một số int có cùng kích thước hoặc thậm chí bạn có thể bỏ giữa chúng mà không thay đổi hoặc làm hỏng giá trị. Mã có thể hoạt động trên hệ thống cụ thể của bạn bằng sự trùng hợp ngẫu nhiên, nhưng nó sẽ phá vỡ trên nhiều hệ thống khác.

Cách duy nhất để bạn có thể xử lý chung cả hai con trỏ và kiểu tích phân (cũng như cấu trúc, cho vấn đề đó) là chuyển các tham số bằng cách sử dụng các danh sách đối số có độ dài thay đổi. Bằng cách đó, bạn có thể trích xuất từng đối số bất kể kích thước của kiểu dữ liệu cơ bản.

Tùy chọn khác là thả cấu trúc chung function và tạo cấu trúc tùy chỉnh cho từng HOF trong mã. Con trỏ hàm có thể liệt kê tất cả các tham số cần thiết trực tiếp thay vì cố gắng tóm tắt chúng sau giao diện chung, điều này sẽ loại bỏ các phôi có vấn đề và cho phép mã hoạt động như mong đợi bất kể các kiểu dữ liệu được sử dụng.

Tính khả dụng, hãy xem xét thay đổi giao diện của bạn để loại trả về HOF được chỉ định (hoặc ít nhất được liệt kê) trên dòng được khai báo. Các đối tượng C luôn liệt kê loại của chúng trong khai báo. Trong ví dụ của bạn, khai báo là function add1 = add(1);. Để tìm ra kiểu dữ liệu mà hàm này trả về, bạn phải tìm hiểu định nghĩa cho HOF. Đây không phải là một nhiệm vụ khó khăn đối với mã mẫu của bạn, nhưng điều này có thể không tầm thường đối với mã phức tạp hơn. Bạn có thể không có mã cho HOF ở tất cả nếu nó đến từ một thư viện. Có lẽ một cái gì đó như function(int) add1 = add(1); có thể rõ ràng hơn.

Lưu ý phụ, tôi khuyên bạn nên xác định các chức năng được tạo tự động là static để giúp ngăn chặn xung đột tên giữa các mô-đun.

+0

Nhận xét rất hữu ích. Cảm ơn! Tôi sẽ cố gắng cập nhật chiến lược dịch thuật của mình. –

Các vấn đề liên quan