C で lisp のmember-ifごっこ(lambdaっぽいこと)
2007/11/30
今度は、common lisp の member-if に似たことをしようとしてみました。
1: member_if(it, list, new_list, 2: Point* p = it->element; 3: if (p->x % 2 == 0) nil; 4: );
2,3行目が、lambdaに相当する部分です。本当は、lambdaの値が、nilでない場合のリストを作成したかったんですが、値を返すことができなかったので、要素(it->element)が、リスト(list)のメンバーでない場合、nil と書くという決まりです。nilは、ただのcontinueです。continueすることで、リストにaddされなくなるという種です。2,3行目は、具体的には、Point構造体のxの値が偶数の場合、nil(continue)してます。その結果、new_listにPoint構造体のxの値が奇数の要素だけが、格納されます。
foreachは、http://d.hatena.ne.jp/smeghead/20071128/linkedlist と同じなので重複する部分が多いですm(_ _)m
#include <stdio.h> #include <stdlib.h> void* xalloc(size_t sz) { void* p; p = calloc(1, sz); if (!p) exit(1); return p; } /* Iterator */ typedef struct _iterator { void* element; struct _iterator* next; } Iterator; /* Linked List */ typedef struct _list { int element_size; struct _iterator* head; struct _iterator* tail; } List; /* Linked Listの生成 */ #define list_alloc(list, type) \ list = xalloc(sizeof(List)); \ list->element_size = sizeof(type); /* Iteratorに格納する要素のalloc */ void* list_new_element(List* list) { return xalloc(list->element_size); } /* Iteratorの取得 */ Iterator* get_iterator(List* list) { return list->head; } /* 要素を持っているか? */ int iterator_has_value(Iterator* ite) { return ite != NULL; } /* 次の要素を取得する */ void* iterator_next(Iterator* ite) { return ite->next; } /* Linked Listにオブジェクトを追加する */ void list_add(List* list, void* new_element) { if (list->tail == NULL) { /* 最初の要素 */ list->tail = list->head = (Iterator*)xalloc(sizeof(Iterator)); } else { Iterator* old_tail = list->tail; old_tail->next = xalloc(sizeof(Iterator)); list->tail = old_tail->next; } list->tail->element = new_element; } /* Linked Listを開放する */ void list_free(List* list) { Iterator* it = get_iterator(list); while (1) { Iterator* old_it = it; if (it == NULL) break; if (it->element != NULL) free(it->element); it = it->next; free(old_it); } } /* なんちゃって foreach */ #define foreach(it, list) for(it = get_iterator(list); iterator_has_value(it); it = iterator_next(it)) /* なんちゃって member_if */ #define member_if(it, list, new_list, lambda) \ new_list = xalloc(sizeof(List)); \ new_list->element_size = list->element_size; \ foreach (it, list) { \ lambda \ list_add(new_list, it->element); \ } #define nil continue; typedef struct _point { int x; int y; } Point; int main(int argc, char** argv) { List* list; List* new_list; Iterator* it; int i; list_alloc(list, Point); /* リストを作る */ for (i = 0; i < 10; i++) { Point* p1 = list_new_element(list); /* リストに格納するelementの領域を貰う */ p1->x = i; p1->y = i; list_add(list, p1); /* Point構造体をリストに追加する */ } /* member_ifマクロで、2で割って割りきれる場合は、continueするようにした。continueしない場合に、新しいListにaddされる仕組み */ member_if(it, list, new_list, Point* p = it->element; if (p->x % 2 == 0) nil; ); /* 新しいリストを foreach で表示 */ foreach (it, new_list) { Point* p = it->element; printf("point: (%d, %d)\n", p->x, p->y); } list_free(list); /* list_free(new_list); 重複してfreeすると落ちるのはわかったんですが、list_free の修正はさぼりました。 */ return 0; }
実行結果
$ gcc -Wall member_if.c $ ./a.exe point: (1, 1) point: (3, 3) point: (5, 5) point: (7, 7) point: (9, 9)
もう、かなり誤魔化しが目立ってきました。
lispを触った後だと、C のマクロも違って見える。もっとバカなこともできそうな気がしてきますが、私のCの知識ではやはり限界です。
追記:不要なキャストを消しました。