มีเครื่องมืออะไรบ้างสำหรับการเขียนโปรแกรมการทำงานใน C


153

ฉันได้คิดมากเกี่ยวกับวิธีการทำโปรแกรมฟังก์ชั่นใน C ( ไม่ใช่ C ++) เห็นได้ชัดว่า C เป็นภาษาขั้นตอนและไม่สนับสนุนการเขียนโปรแกรมเชิงฟังก์ชันจริงๆ

มีส่วนขยายคอมไพเลอร์ / ภาษาที่เพิ่มโครงสร้างการเขียนโปรแกรมการทำงานบางอย่างให้กับภาษาหรือไม่ GCC มีฟังก์ชั่นที่ซ้อนกันเป็นส่วนขยายภาษา ฟังก์ชั่นที่ซ้อนกันสามารถเข้าถึงตัวแปรจากเฟรมหลักของสแต็กหลักได้

ตัวอย่างเช่นสิ่งหนึ่งที่ฉันคิดว่าอาจมีประโยชน์จริง ๆ ใน C คือที่ใดก็ตามที่คาดว่าจะมีตัวชี้ฟังก์ชั่นคุณสามารถส่งผ่านการแสดงออกแลมบ์ดา C ++ 0x กำลังจะรวมแลมบ์ดานิพจน์ (ซึ่งฉันคิดว่ามันยอดเยี่ยม); อย่างไรก็ตามฉันกำลังมองหาเครื่องมือที่ใช้กับ C ตรง

[แก้ไข] เพื่อชี้แจงฉันไม่พยายามแก้ปัญหาเฉพาะใน C ที่เหมาะกับการเขียนโปรแกรมการทำงานมากกว่า ฉันแค่อยากรู้เกี่ยวกับเครื่องมือที่ออกมีถ้าฉันต้องการที่จะทำ


1
แทนที่จะมองหาแฮ็กและส่วนขยายที่ไม่ใช่แบบพกพาเพื่อลองเปลี่ยน C ให้เป็นสิ่งที่ไม่ใช่ทำไมคุณไม่ใช้ภาษาที่ให้ฟังก์ชั่นที่คุณต้องการ
Robert Gamble

ดูคำถามที่เกี่ยวข้องเพิ่มเติม: < stackoverflow.com/questions/24995/… >
Andy Brice

@AndyBrice คำถามนั้นเกี่ยวกับภาษาที่แตกต่างกันและดูเหมือนจะไม่สมเหตุสมผล (C ++ ไม่มี "ระบบนิเวศ" ในความหมายเดียวกับ
Kyle Strand

คำตอบ:


40

FFCALLช่วยให้คุณสร้างปิดใน C - callback = alloc_callback(&function, data)ผลตอบแทนที่เป็นตัวชี้ฟังก์ชั่นดังกล่าวว่าจะเทียบเท่ากับการโทรcallback(arg1, ...) function(data, arg1, ...)คุณจะต้องจัดการกับการเก็บขยะด้วยตนเองแม้ว่า

เกี่ยวเนื่องบล็อกได้รับการเพิ่มส้อมแอปเปิ้ลของ GCC; พวกมันไม่ใช่พอยน์เตอร์ที่ทำงานได้ แต่มันช่วยให้คุณผ่านรอบ lambdas ในขณะที่หลีกเลี่ยงความจำเป็นในการสร้างและจัดเก็บฟรีสำหรับตัวแปรที่จับด้วยมือ (มีประสิทธิภาพการคัดลอกและการนับการอ้างอิงเกิดขึ้น


89

คุณสามารถใช้ฟังก์ชั่นที่ซ้อนกันของ GCC เพื่อจำลองการแสดงออกแลมบ์ดาในความเป็นจริงฉันมีแมโครที่ทำเพื่อฉัน:

#define lambda(return_type, function_body) \
  ({ \
    return_type anon_func_name_ function_body \
    anon_func_name_; \
  })

ใช้แบบนี้:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });

12
นี่มันเจ๋งจริงๆ ดูเหมือนว่า__fn__เป็นชื่อโดยพลการสำหรับฟังก์ชันที่กำหนดไว้ในบล็อก({... })ไม่ใช่ส่วนขยาย GCC หรือมาโครที่กำหนดไว้ล่วงหน้าใช่ไหม การเลือกชื่อ__fn__(ดูคล้ายกับคำจำกัดความของ GCC มาก) ทำให้ฉันเกาหัวและค้นหาเอกสาร GCC โดยไม่มีผลดี
FooF

1
น่าเศร้าที่นี่ใช้งานไม่ได้: หากคุณต้องการปิดการจับสิ่งใด ๆ ก็ต้องใช้กองซ้อนที่ปฏิบัติการได้ซึ่งเป็นวิธีปฏิบัติด้านความปลอดภัยที่น่ากลัว โดยส่วนตัวฉันไม่คิดว่ามันมีประโยชน์เลย
Demi

มันไม่ได้มีประโยชน์จากระยะไกล แต่มันสนุก นั่นเป็นเหตุผลที่ยอมรับคำตอบอื่น ๆ
Joe D

65

ฟังก์ชั่นการเขียนโปรแกรมไม่ได้เกี่ยวกับ lambdas มันคือทั้งหมดที่เกี่ยวกับฟังก์ชั่นบริสุทธิ์ ดังนั้นต่อไปนี้ในวงกว้างส่งเสริมสไตล์การทำงาน:

  1. ใช้อาร์กิวเมนต์ของฟังก์ชันเท่านั้นห้ามใช้สถานะโกลบอล

  2. ลดผลข้างเคียงเช่น printf หรือ IO ใด ๆ ส่งคืนข้อมูลที่อธิบายถึง IO ซึ่งสามารถดำเนินการได้แทนที่จะทำให้เกิดผลข้างเคียงโดยตรงในทุกฟังก์ชั่น

สามารถทำได้ในที่ราบคไม่จำเป็นต้องใช้เวทย์มนตร์


5
ฉันคิดว่าคุณได้นำมุมมองสุดยอดของการเขียนโปรแกรมที่ใช้งานได้มาสู่จุดที่ไร้สาระ อะไรคือคุณสมบัติที่บริสุทธิ์เช่นmapถ้าไม่มีสิ่งอำนวยความสะดวกในการส่งผ่านฟังก์ชั่น?
Jonathan Leonard

27
ฉันคิดว่าวิธีนี้มีประโยชน์มากกว่าการใช้มาโครในการสร้างภาษาที่ใช้งานได้ในค การใช้ฟังก์ชั่นที่บริสุทธิ์อย่างเหมาะสมมีแนวโน้มที่จะปรับปรุงระบบมากกว่าการใช้แผนที่แทนที่จะเป็นลูป
Andy จนถึง

6
แน่นอนคุณรู้ว่าแผนที่เป็นเพียงจุดเริ่มต้น หากไม่มีฟังก์ชั่นชั้นหนึ่งโปรแกรมใด ๆ (แม้ว่าฟังก์ชั่นทั้งหมดจะ 'บริสุทธิ์') จะมีลำดับต่ำกว่า (ในแง่ของทฤษฎีข้อมูล) กว่าที่เทียบเท่ากับฟังก์ชั่นชั้นหนึ่ง ในมุมมองของฉันมันเป็นสไตล์การประกาศนี้เป็นไปได้เฉพาะกับฟังก์ชั่นชั้นหนึ่งที่เป็นประโยชน์หลักของ FP ฉันคิดว่าคุณกำลังก่อความเสียหายให้กับใครบางคนเพื่อแสดงให้เห็นว่าการใช้คำฟุ่มเฟื่อยที่เกิดจากการขาดฟังก์ชั่นชั้นหนึ่งเป็นสิ่งที่ FP มีให้ ควรสังเกตว่าในอดีตคำว่า FP หมายถึงฟังก์ชั่นชั้นหนึ่งมากกว่าความบริสุทธิ์
Jonathan Leonard

PS ความคิดเห็นก่อนหน้านี้มาจากมือถือ - ไวยากรณ์ผิดปกติไม่ได้ตั้งใจ
Jonathan Leonard

1
คำตอบของ Andy Tills แสดงมุมมองที่เป็นประโยชน์ต่อสิ่งต่าง ๆ การไปที่ "บริสุทธิ์" ใน C ไม่ต้องการอะไรที่แฟนซีและให้ประโยชน์ที่ยอดเยี่ยม ฟังก์ชั่นชั้นหนึ่งในบัญชีเปรียบเทียบกับ "การใช้ชีวิตอย่างสะดวกสบาย" มันสะดวก แต่การใช้รูปแบบการเขียนโปรแกรมที่บริสุทธิ์นั้นเป็นจริง ฉันสงสัยว่าทำไมไม่มีใครพูดถึงการเรียกหางที่สัมพันธ์กับสิ่งนี้ทั้งหมด ผู้ที่ต้องการฟังก์ชั่นชั้นหนึ่งควรขอโทรหาง
BitTickler

17

หนังสือฟังก์ชั่นของ Hartel & Muller สามารถทำงานได้ในปัจจุบัน (2012-01-02) ที่: http://eprints.eemcs.utwente.nl/1077/ (มีลิงค์ไปยังเวอร์ชั่น PDF)


2
ไม่มีความพยายามในการทำงานใด ๆ ในหนังสือเล่มนี้ (ตามที่เกี่ยวข้องกับคำถาม)
Eugene Tolmachev

4
ดูเหมือนคุณจะพูดถูก อันที่จริงคำนำระบุวัตถุประสงค์ของหนังสือเล่มนี้คือการสอนการเขียนโปรแกรมที่จำเป็นหลังจากที่นักเรียนได้ทำความคุ้นเคยกับการเขียนโปรแกรมการทำงานแล้ว FYI นี่คือคำตอบแรกของฉันใน SO และมันถูกเขียนเป็นคำตอบเท่านั้นเพราะฉันไม่ได้ต้องการชื่อเสียงที่จะแสดงความคิดเห็นคำตอบก่อนหน้าด้วยลิงก์เน่าเสีย ฉันมักจะสงสัยว่าทำไมคนถึง +1 คำตอบนี้ ... โปรดอย่าทำอย่างนั้น! :-)
FooF

1
บางทีหนังสือน่าจะดีกว่าเรียกว่า Post-Functional Programming (ก่อนอื่นเพราะ "Imperative C" ไม่ฟังดูเซ็กซี่ประการที่สองเพราะมันมีความคุ้นเคยกับกระบวนทัศน์การเขียนโปรแกรมที่ใช้งานได้ประการที่สามเป็นปุนเพราะกระบวนทัศน์ที่จำเป็น ฟังก์ชั่นที่ถูกต้องและอาจเป็นสี่อ้างอิงถึงแนวคิดของ Larry Wall เกี่ยวกับการเขียนโปรแกรมหลังสมัยใหม่ - แม้ว่าหนังสือเล่มนี้จะถูกเขียนก่อนหน้าบทความ / งานนำเสนอของ Larry Wall)
FooF

และนี่คือคำตอบที่ซ้ำกัน
PhilT

8

สิ่งที่จำเป็นต้องมีสำหรับสไตล์การเขียนโปรแกรมการทำงานคือฟังก์ชันชั้นหนึ่ง มันอาจจำลองในพกพา C ถ้าคุณทนต่อไป:

  • การจัดการด้วยตนเองของการผูกขอบเขตคำศัพท์หรือการปิด
  • การจัดการด้วยตนเองของอายุการใช้งานฟังก์ชันตัวแปร
  • ไวยากรณ์ทางเลือกของแอปพลิเคชั่น / การโทร
/* 
 * with constraints desribed above we could have
 * good approximation of FP style in plain C
 */

int increment_int(int x) {
  return x + 1;
}

WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS(increment, increment_int);

map(increment, list(number(0), number(1)); // --> list(1, 2)


/* composition of first class function is also possible */

function_t* computation = compose(
  increment,
  increment,
  increment
);

*(int*) call(computation, number(1)) == 4;

รันไทม์สำหรับรหัสดังกล่าวอาจมีขนาดเล็กเท่ากับหนึ่งด้านล่าง

struct list_t {
  void* head;
  struct list_t* tail;
};

struct function_t {
   void* (*thunk)(list_t*);
   struct list_t* arguments;
}

void* apply(struct function_t* fn, struct list_t* arguments) {
  return fn->thunk(concat(fn->arguments, arguments));
}

/* expansion of WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS */
void* increment_thunk(struct list_t* arguments) {
  int x_arg = *(int*) arguments->head;
  int value = increment_int(x_arg);
  int* number = malloc(sizeof *number);

  return number ? (*number = value, number) : NULL;
}

struct function_t* increment = &(struct function_t) {
  increment_thunk,
  NULL
};

/* call(increment, number(1)) expands to */
apply(increment, &(struct list_t) { number(1), NULL });

ในสาระสำคัญเราเลียนแบบฟังก์ชั่นชั้นหนึ่งด้วยการปิดที่แสดงเป็นคู่ของฟังก์ชั่น / ข้อโต้แย้งบวกพวงของมาโคร รหัสเสร็จสมบูรณ์อาจจะพบได้ที่นี่


7

สิ่งสำคัญที่อยู่ในใจคือการใช้รหัสกำเนิด คุณยินดีที่จะเขียนโปรแกรมในภาษาอื่นที่ให้ฟังก์ชั่นการโปรแกรมและสร้างรหัส C จากนั้นหรือไม่?

หากนั่นไม่ใช่ตัวเลือกที่น่าสนใจคุณสามารถใช้ CPP ในทางที่ผิดเพื่อเข้าไปมีส่วนร่วม ระบบมาโครควรให้คุณเลียนแบบแนวคิดการเขียนโปรแกรมที่ใช้งานได้บางอย่าง ฉันได้ยินมาว่า gcc ใช้งานในลักษณะนี้ แต่ฉันไม่เคยตรวจสอบเลย

แน่นอนว่า C สามารถส่งผ่านฟังก์ชั่นไปรอบ ๆ โดยใช้ตัวชี้ฟังก์ชั่นปัญหาหลักคือการปิดไม่ได้และระบบประเภทมีแนวโน้มที่จะได้รับในทาง คุณสามารถสำรวจระบบมาโครที่มีประสิทธิภาพมากกว่า CPP เช่น M4 ในที่สุดฉันเดาว่าสิ่งที่ฉันแนะนำคือความจริง C ไม่ได้ขึ้นอยู่กับงานโดยไม่ต้องใช้ความพยายามอย่างมาก แต่คุณสามารถขยาย C เพื่อให้เป็นไปตามภารกิจ ส่วนขยายนั้นจะดูเหมือน C มากที่สุดหากคุณใช้ CPP หรือคุณสามารถไปที่ปลายอีกด้านหนึ่งของสเปกตรัมและสร้างรหัส C จากภาษาอื่น ๆ


นี่คือคำตอบที่ซื่อสัตย์ที่สุด การพยายามเขียน C ในลักษณะที่ใช้งานได้คือการต่อสู้กับการออกแบบและโครงสร้างภาษาเอง
josiah

5

หากคุณต้องการติดตั้งระบบปิดคุณจะต้องใช้ภาษาแอสเซมบลีและการแลกเปลี่ยน / การจัดการสแต็ก ไม่แนะนำเลยเพียงแค่บอกว่าเป็นสิ่งที่คุณต้องทำ

ไม่แน่ใจว่าคุณจะจัดการกับฟังก์ชั่นที่ไม่ระบุชื่อใน C. บนเครื่อง von Neumann คุณสามารถทำหน้าที่ที่ไม่ระบุชื่อใน asm ได้อย่างไร



2

ภาษาเฟลิกซ์คอมไพล์ C ++ บางทีนั่นอาจเป็นขั้นตอนสำคัญหากคุณไม่สนใจ C ++


9
⁻¹เพราะα) ผู้เขียนพูดอย่างชัดเจน«ไม่ใช่ C ++ », β) C ++ ที่ผลิตโดยคอมไพเลอร์จะไม่ใช่«มนุษย์ที่อ่านได้ C ++ » γ) มาตรฐาน C ++ รองรับการเขียนโปรแกรมใช้งานได้โดยไม่จำเป็นต้องใช้คอมไพเลอร์จากภาษาหนึ่งไปยังอีกภาษาหนึ่ง
สวัสดีแองเจิล

เมื่อคุณสร้างภาษาที่ใช้งานได้โดยใช้แมโครหรือเหมือนกันคุณจะบอกเป็นนัยว่าคุณไม่สนใจ C มากคำตอบนี้ใช้ได้และไม่เป็นไรเพราะแม้แต่ C ++ ก็เริ่มเป็นปาร์ตี้แมโครที่อยู่ด้านบนของ C หากคุณ ลงไปที่ถนนไกลพอคุณจะจบลงด้วยภาษาใหม่ จากนั้นมันจะลงมาที่การสนทนาทางแบ็คเอนด์เท่านั้น
BitTickler

1

ภาษาการเขียนโปรแกรมค่อนข้างน้อยเขียนด้วยภาษาซีและบางภาษาก็รองรับฟังก์ชั่นในฐานะพลเมืองชั้นหนึ่งภาษาในพื้นที่นั้นคือ ecl (เสียงเรียกเข้าทั่วไปที่ใช้เสียงผิดเพี้ยน IIRC), Gnu Smalltalk (gst) (Smalltalk มีบล็อก) จากนั้นมีห้องสมุด สำหรับ "closures" เช่นใน glib2 http://library.gnome.org/devel/gobject/unstable/chapter-signal.html#closure ซึ่งอย่างน้อยก็เข้าใกล้ฟังก์ชั่นการเขียนโปรแกรม ดังนั้นอาจจะใช้บางส่วนของการใช้งานเพื่อทำหน้าที่การเขียนโปรแกรมอาจเป็นตัวเลือก

หรือคุณสามารถไปเรียนรู้ Ocaml, Haskell, Mozart / Oz หรือชอบ ;-)

ความนับถือ


คุณจะช่วยบอกฉันว่ามีอะไรผิดพลาดมากในการใช้ห้องสมุดซึ่งอย่างน้อยก็สนับสนุนรูปแบบการเขียนโปรแกรม FP
ฟรีดริช

1

วิธีที่ฉันไปเกี่ยวกับการเขียนโปรแกรมฟังก์ชั่นใน C คือการเขียนล่ามภาษาฟังก์ชั่นใน C. ฉันชื่อ Fexl ซึ่งสั้นสำหรับ "Function EXpression Language"

ล่ามมีขนาดเล็กมากรวบรวมได้ถึง 68K ในระบบของฉันด้วยการเปิดใช้งาน -O3 มันไม่ใช่ของเล่นด้วย - ฉันใช้มันสำหรับรหัสการผลิตใหม่ทั้งหมดที่ฉันเขียนให้กับธุรกิจของฉัน (การบัญชีผ่านเว็บสำหรับพันธมิตรการลงทุน)

ตอนนี้ฉันเขียนรหัส C เพียงเพื่อ (1) เพิ่มฟังก์ชั่นในตัวที่เรียกรูทีนระบบ (เช่น fork, exec, setrlimit ฯลฯ ) หรือ (2) ปรับฟังก์ชั่นที่สามารถเขียนใน Fexl (เช่นการค้นหา) สำหรับสตริงย่อย)

กลไกโมดูลขึ้นอยู่กับแนวคิดของ "บริบท" บริบทคือฟังก์ชัน (เขียนเป็น Fexl) ซึ่งแมปสัญลักษณ์กับคำนิยามของมัน เมื่อคุณอ่านไฟล์ Fexl คุณสามารถแก้ไขได้ด้วยบริบทที่คุณต้องการ สิ่งนี้ช่วยให้คุณสร้างสภาพแวดล้อมที่กำหนดเองหรือเรียกใช้รหัสใน "sandbox" แบบ จำกัด

http://fexl.com


-3

มันเกี่ยวกับ C ที่คุณต้องการทำให้การทำงานไวยากรณ์หรือความหมายคืออะไร? ความหมายของการเขียนโปรแกรมเชิงหน้าที่สามารถเพิ่มลงใน C compiler ได้อย่างแน่นอน แต่เมื่อถึงเวลาที่คุณทำเสร็จแล้วคุณจะมีภาษาเทียบเท่ากับหนึ่งในภาษาที่ใช้งานได้เช่น Scheme, Haskell เป็นต้น

มันจะเป็นการใช้เวลาที่ดีกว่าเพื่อเรียนรู้ไวยากรณ์ของภาษาเหล่านั้นซึ่งสนับสนุนความหมายโดยตรง


-3

ไม่ทราบเกี่ยวกับ C. มีคุณสมบัติการทำงานบางอย่างใน Objective-C แม้ว่า GCC บน OSX ยังรองรับคุณสมบัติบางอย่าง แต่ฉันขอแนะนำอีกครั้งให้เริ่มใช้ภาษาที่ใช้งานได้ โดยส่วนตัวฉันเริ่มต้นจากแบบแผนมีหนังสือที่ยอดเยี่ยมเช่น The Little Schemer ที่สามารถช่วยคุณได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.