C'de fonksiyonel programlama için hangi araçlar var?


153

Son zamanlarda C ( değil C ++) fonksiyonel programlama yapmaya gitmek hakkında çok düşünüyordum . Açıkçası, C prosedürel bir dildir ve doğal olarak işlevsel programlamayı gerçekten desteklemez.

Dile bazı işlevsel programlama yapıları ekleyen herhangi bir derleyici / dil uzantısı var mı? GCC, dil uzantısı olarak iç içe geçmiş işlevler sağlar ; yuvalanmış işlevler üst yığın çerçevesinden değişkenlere erişebilir, ancak bu hala olgun kapanışlardan çok uzaktadır.

Örneğin, C'de gerçekten yararlı olabileceğini düşündüğüm bir şey, bir işlev işaretçisinin beklendiği her yerde, bir lambda ifadesini geçerek bir işlev işaretçisine çürüyen bir kapatma oluşturabileceğinizdir. C ++ 0x (harika olduğunu düşünüyorum) lambda ifadeleri içerecek; Ancak, düz C için geçerli araçlar arıyorum.

Açıklamak için, fonksiyonel programlama için daha uygun C belirli bir sorunu çözmeye çalışmıyorum; Bunu yapmak istersem sadece dışarıda hangi araçların olduğunu merak ediyorum.


1
C'yi olmayan bir şeye dönüştürmek için kesmek ve taşınabilir olmayan uzantılar aramak yerine, neden sadece aradığınız işlevselliği sağlayan bir dil kullanmıyorsunuz?
Robert Gamble

Ayrıca ilgili soruya bakın: < stackoverflow.com/questions/24995/… >
Andy Brice

@AndyBrice Bu soru farklı bir dil ile ilgili ve aslında mantıklı gelmiyor (C ++ ile aynı anlamda bir "ekosistem" yok.
Kyle Strand

Yanıtlar:


40

FFCALL , C'de kapaklar oluşturmanıza olanak tanır - çağrıya eşdeğer callback = alloc_callback(&function, data)bir işlev işaretçisi döndürür . Bununla birlikte, çöp toplama işlemini elle halletmeniz gerekecektir.callback(arg1, ...)function(data, arg1, ...)

Buna bağlı olarak, Apple'ın GCC çatalına bloklar eklendi; bunlar işlev işaretçileri değildir, ancak yakalanan değişkenler için elle depolama ve depolama ihtiyacını ortadan kaldırırken lambdaların etrafından geçmenize izin verir (etkili, bazı sözdizimsel şeker ve çalışma zamanı kütüphanelerinin arkasına gizlenmiş bazı kopyalama ve referans sayımı gerçekleşir).


89

Lambda ifadelerini simüle etmek için GCC'nin iç içe geçmiş işlevlerini kullanabilirsiniz, aslında benim için yapacak bir makrom var:

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

Bunun gibi kullanın:

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

12
Bu gerçekten havalı. Öyle görünüyor __fn__bloğu içinde tanımlanan fonksiyon için sadece keyfi adıdır ({... }), bazı GCC uzantısı veya önceden tanımlanmış makro? __fn__(GCC tanımına çok benzeyen) için isim seçimi gerçekten başımı çizdi ve iyi bir etki için GCC belgelerini aradı.
FooF

1
Ne yazık ki, bu pratikte işe yaramıyor: kapağın bir şey yakalamasını istiyorsanız, korkunç bir güvenlik uygulaması olan yürütülebilir bir yığın gerektirir. Şahsen, bunun yararlı olduğunu düşünmüyorum.
Demi

Uzaktan yararlı değil, ama eğlenceli. Bu yüzden diğer cevap kabul edilir.
Joe D

65

Fonksiyonel programlama lambdalarla değil, tamamen saf fonksiyonlarla ilgilidir. Yani aşağıdakiler genel olarak işlevsel stili teşvik eder:

  1. Yalnızca işlev bağımsız değişkenlerini kullanın, genel durumu kullanmayın.

  2. Yan etkileri azaltın, örneğin printf veya herhangi bir IO. Doğrudan tüm işlevlerde yan etkilere neden olmak yerine yürütülebilen G / Ç'yi tanımlayan verileri döndürün.

Bu düz c'de elde edilebilir, sihire gerek yoktur.


5
Bence fonksiyonel programlama konusundaki aşırı görüşü saçmalık noktasına kadar götürdünüz. Örneğin, mapbir işlevi ona iletecek olanaklara sahip değilse , saf spesifikasyonun ne faydası vardır ?
Jonathan Leonard

27
Bence bu yaklaşım, içinde fonksiyonel bir dil oluşturmak için makroları kullanmaktan çok daha pragmatiktir c. Saf işlevlerin uygun şekilde kullanılması, sistemi iyileştirmek döngüler yerine harita kullanmaktan daha olasıdır.
Andy Till

6
Şüphesiz, haritanın sadece başlangıç ​​noktası olduğunu biliyorsunuz. Birinci sınıf işlevler olmadan, herhangi bir program (tüm işlevleri 'saf' olsa bile) birinci sınıf işlevlere eşdeğerden daha düşük mertebeden (bilgi teorisi anlamında) olacaktır. Benim görüşüme göre, bu bildirim tarzı sadece FP'nin ana yararı olan birinci sınıf işlevlerle mümkün. Birinci sınıf fonksiyonların eksikliğinden kaynaklanan ayrıntıların tüm FP'lerin sunduğu anlamına geldiğini düşünmek için birine kötülük yapıyorsunuz. Tarihsel olarak FP teriminin birinci sınıf işlevlerden saflıktan daha fazlasını ima ettiği unutulmamalıdır.
Jonathan Leonard

PS Önceki yorum mobil - düzensiz gramer kasıtlı değildi.
Jonathan Leonard

1
Andy Tills cevabı, şeyler hakkında çok pragmatik bir bakış sergiliyor. C'de "Saf" olmak fantezi bir şey gerektirmez ve büyük bir fayda sağlar. Birinci sınıf fonksiyonlar, "rahat yaşam" ı karşılaştırır. Bu uygundur, ancak saf bir programlama tarzını benimsemek pratiktir. Acaba neden kimse bununla ilgili kuyruk çağrılarını tartışmıyor. Birinci sınıf fonksiyon isteyenler ayrıca kuyruk çağrıları istemelidir.
BitTickler

17

Hartel & Muller'in kitabı, Fonksiyonel C , günümüzde (2012-01-02) şu adreste bulunabilir: http://eprints.eemcs.utwente.nl/1077/ (PDF sürümüne bir bağlantı vardır).


2
Bu kitapta işlevsel olan herhangi bir şey için herhangi bir girişim yoktur (soru ile ilgili olarak).
Eugene Tolmachev

4
Görünüşe göre oldukça haklısın. Aslında önsöz, kitabın amacının, öğrenci fonksiyonel programlamaya zaten alıştıktan sonra zorunlu programlamayı öğretmek olduğunu belirtir. FYI, bu benim SO'daki ilk cevabımdı ve sadece çürümüş bağlantı ile önceki bir cevabı yorumlamak için itibar gerektirmediğim için bir cevap olarak yazılmıştır. İnsanların neden bu cevabı + 1'lediğini merak ediyorum ... Lütfen bunu yapma! :-)
FooF

1
Belki de kitap Post-Functional Programming olarak adlandırılmalıdır (her şeyden önce "Imperative C" seksi görünmüyor çünkü ikincisi fonksiyonel programlama paradigmasına aşinalık, üçüncü olarak bir pun olarak kabul edilir çünkü zorunlu paradigma standartların düşüşüne benziyor ve doğru işlevsellik ve belki de dördüncü olarak Larry Wall'un postmodern programlama kavramına atıfta bulunmaktayız - bu kitap Larry Wall'un makalesinden / sunumundan önce yazılmıştır).
FooF

Ve bu yinelenen bir cevap
PhilT

8

Fonksiyonel programlama stili için ön koşul birinci sınıf bir fonksiyondur. Bir sonraki tahammül ederseniz, taşınabilir C'de simüle edilebilir:

  • sözcüksel kapsam bağlarının elle yönetilmesi, aka kapanışları.
  • fonksiyon değişkenlerinin ömür boyu manuel yönetimi.
  • işlev uygulama / çağrısının alternatif sözdizimi.
/* 
 * 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;

böyle bir kod için çalışma zamanı aşağıdaki kadar küçük olabilir

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 });

Özünde, birinci sınıf işlevi, işlev / argüman çifti ve bir grup makro olarak temsil edilen kapaklarla taklit ediyoruz. Kodun tamamını burada bulabilirsiniz .


7

Akla gelen ana şey kod üreteçlerinin kullanılmasıdır. Fonksiyonel programlamayı sağlayan farklı bir dilde programlayıp daha sonra bundan C kodunu oluşturmak ister misiniz?

Bu çekici bir seçenek değilse, oradan yol almak için CPP'yi kötüye kullanabilirsiniz. Makro sistemi, bazı işlevsel programlama fikirlerini taklit etmenize izin vermelidir. Gcc'nin bu şekilde uygulandığını söyledim ama hiç kontrol etmedim.

C, elbette, fonksiyon işaretleyicilerini kullanarak fonksiyonları geçebilir, ana problemler kapanma eksikliğidir ve tip sistemi yoluna girme eğilimindedir. M4 gibi CPP'den daha güçlü makro sistemleri keşfedebilirsiniz. Nihayetinde, önerdiğim şey, gerçek C'nin büyük çaba sarf etmeden göreve gelmediğidir, ancak C'yi göreve uygun hale getirebilirsiniz. CPP kullanırsanız veya spektrumun diğer ucuna gidip başka bir dilden C kodu oluşturabilirseniz, bu uzantı en çok C'ye benzeyecektir.


Bu en dürüst cevap. C'yi işlevsel bir şekilde yazmaya çalışmak, dil tasarımına ve yapısına karşı savaşmaktır.
josiah

5

Kapakları uygulamak istiyorsanız, montaj dili ve yığın takas / yönetimi ile uğraşmanız gerekir. Buna karşı tavsiye değil, sadece yapmanız gereken budur.

C'de anonim işlevleri nasıl ele alacağınızdan emin değilsiniz. Bir von Neumann makinesinde anonim işlevleri asm'de de yapabilirsiniz.



2

Felix dil C ++ için derler. C ++ 'a aldırmazsanız belki de bu bir adım taşı olabilir.


9
⁻¹ çünkü α) Yazar açıkça «C ++ değil», β) Bir derleyici tarafından üretilen C ++, «insan tarafından okunabilir C ++» olamaz. ++) C ++ standardı işlevsel bir programlamayı destekler, bir dilden diğerine derleyici kullanmaya gerek yoktur.
Hi-Angel

Makrolar veya benzerleri aracılığıyla işlevsel bir dil bulduğunuzda, C için çok fazla umursamadığınızı otomatik olarak ima edersiniz. Bu cevap geçerli ve tamam çünkü C ++ bile C'nin üstünde bir makro partisi olarak başladı. o yoldan yeterince aşağı inerseniz, yeni bir dil elde edersiniz. Sonra sadece bir arka plan tartışmasına geliyor.
BitTickler

1

Pek çok programlama dili C dilinde yazılmıştır. Ve bazıları birinci sınıf vatandaşlar olarak işlevleri desteklemektedir, o bölgedeki diller ecl (embbedabble common lisp IIRC), Gnu Smalltalk (gst) (Smalltalk blokları vardır), o zaman kütüphaneler var "kapanmalar" için örneğin glib2'de http://library.gnome.org/devel/gobject/unstable/chapter-signal.html#closure en azından işlevsel programlamaya içinde. Bu yüzden belki bu uygulamalardan bazılarını fonksiyonel programlama yapmak için kullanmak bir seçenek olabilir.

Eh ya da Ocaml, Haskell, Mozart / Oz veya benzeri ;-) öğrenmeye gidebilirsiniz.

Saygılarımızla


En azından FP programlama tarzını destekleyen kütüphaneleri kullanmanın ne kadar korkunç bir şey olduğunu söylemek ister misiniz?
Friedrich

1

C'de fonksiyonel programlama yapmaya başladığım şekilde C'ye bir fonksiyonel dil tercümanı yazmaktı. Buna Fexl adını verdim.

Tercüman çok küçük, -O3 etkinken sistemimde 68K'ya kadar derleniyor. Bu bir oyuncak da değil - işim için yazdığım tüm yeni üretim kodları için kullanıyorum (yatırım ortaklıkları için web tabanlı muhasebe).

Şimdi C kodunu sadece (1) bir sistem rutini çağıran yerleşik bir işlev eklemek için yazıyorum (örneğin, fork, exec, setrlimit, vb.) Veya (2) Fexl'de yazılabilecek bir işlevi optimize etme (ör. Arama bir alt dize için).

Modül mekanizması bir "bağlam" kavramına dayanmaktadır. Bağlam, bir sembolü tanımıyla eşleyen bir işlevdir (Fexl ile yazılmış). Bir Fexl dosyasını okuduğunuzda, istediğiniz herhangi bir içerikle çözebilirsiniz. Bu, özel ortamlar oluşturmanıza veya kısıtlanmış bir "sanal alanda" kod çalıştırmanıza olanak tanır.

http://fexl.com


-3

C ile ilgili işlevsel yapmak istediğiniz sözdizimi, sözdizimi veya anlambilim nedir? İşlevsel programlamanın anlambilimi kesinlikle C derleyicisine eklenebilir, ancak tamamlandığında, Şema, Haskell vb. Gibi mevcut işlevsel dillerden birine eşdeğer olacaksınız.

Doğrudan bu semantiği destekleyen sözdizimini öğrenmek daha iyi bir zaman olacaktır.


-3

Objective-C'de bazı işlevsel özellikler olsa da, OSX'teki GCC bazı özellikleri de destekliyor, ancak yine de fonksiyonel bir dil kullanmaya başlamanızı tavsiye ederim, yukarıda belirtilen çok şey var. Şahsen şema ile başladım, Küçük Schemer gibi size yardımcı olabilecek bazı mükemmel kitaplar var.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.