C で lisp のmember-ifごっこ(lambdaっぽいこと)

今度は、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の知識ではやはり限界です。

追記:不要なキャストを消しました。

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.