2012/01/17


trieなんたらが話題になってたのでなんとなく書いてみた。
ベンチとかはやってない。
404 Blog Not Found:Algorithm - 連想配列の実装としてのハッシュはオワコン?

そのデータ構造は、君の魂を差し出すに足るものかい? 連想配列( Associative array )がコレクション( Collection )、すなわち数多のデータ構造をまとめるデータ構造としての覇...

http://blog.livedoor.jp/dankogai/archives/51765855.html
#include <stdio.h>
#include <stdlib.h>

typedef struct _trie {
  char c;
  unsigned int n;
  struct _trie** next;
  void* value;
} trie;

trie*
trie_new() {
  trie* p = (trie*) malloc(sizeof(trie));
  p->c = 0;
  p->n = 0;
  p->next = NULL;
  return p;
}

void
trie_free(trie* p) {
  unsigned int i;
  for (i = 0; i < p->n; i++)
    trie_free(p->next[i]);
  if (p->n)
    free(p->next);
  free(p);
}

trie*
trie_put(trie* p, const char* key, const void* value) {
  if (*key == 0) {
    p->value = (void*) value;
    return p;
  }
  trie* next = NULL;
  int i;
  for (i = 0; i < p->n; i++)
    if (p->next[i]->c == *key) {
      next = p;
      break;
    }
  if (!next) {
    if (!(next = trie_new())) return NULL;
    trie** children = (trie**) realloc(p->next, p->n * sizeof(trie*));
    if (!children) return NULL;
    p->next = children;

    next->c = *key;
    p->next[p->n] = next;
    p->n++;
  }
  return trie_put(next, key+1, value);
}

trie*
trie_get(trie* p, const char* key) {
  if (p->c) {
    if (p->c != *key)
      return NULL;
    if (p->c == *key && *(key+1) == 0)
      return p;
    key++;
  }
  int i;
  trie* value = NULL;
  for (i = 0; i < p->n; i++) {
    if ((value = trie_get(p->next[i], key)))
      return value;
  }
  return NULL;
}

void
safe_puts(const char* key, const trie* p) {
  if (!p)
    printf("%s not found\n", key);
  else if (!p->value)
    printf("%s: null\n", key);
  else
    printf("%s%s\n", key, (char*) p->value);
}

int
main(int argc, char* argv[]) {
  trie* p = trie_new();
  trie* v;

  trie_put(p, "foo""bar");
  trie_put(p, "bar""baz");

  v = trie_get(p, "baz");
  safe_puts("baz", v);

  v = trie_get(p, "foo");
  safe_puts("foo", v);

  v = trie_get(p, "bar");
  safe_puts("bar", v);

  trie_put(p, "うんこ""うんこっこー");
  v = trie_get(p, "うんこ");
  safe_puts("うんこ", v);

  v = trie_get(p, "404 blog");
  safe_puts("404 blog", v);

  trie_free(p);
  return 0;
}
baz not found
foo: bar
bar: baz
うんこ: うんこっこー
404 blog not found
Posted at by



2012/01/13


追記: 良く見たらstrncpyの罠でもなんでもなく、バッファがクリアされてないのが原因だった。って事であとでpullreqでも送っとくかな。

まぁソート関数自身の問題じゃないので控えめに。

strncpyは必ず \0 で埋めてくれるとは限らない。
404 Blog Not Found:algorithm - bucketsort.[ch] - 汎用かつlibcの*sortより高速な

dankogai/c-bucketsort - GitHub

http://blog.livedoor.jp/dankogai/archives/51764911.html
dankogai/c-bucketsort - GitHub

bucketsort - bucket sort that can be used for general purpose

https://github.com/dankogai/c-bucketsort
strncpy(3)のmanページにはこう書いてある。

The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy.

The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.

なので... # ./bucketsort README とかするとNULストップしてないバッファで画面が賑やかになり、時にはけたたましいビープ音で心躍る。

サンプルプログラムの仕様が最終行の改行コードなしでも動く様にすべきなのかどうなのかが分からないので diff --git a/main.c b/main.c
index 67e4e00..48391b4 100644
--- a/main.c
+++ b/main.c
@@ -74,6 +74,7 @@ int main(int argc, char **argv)
        if (!lines[lcur])
            EXIT("malloc failed");
        strncpy(lines[lcur], buf, slen - 1);    // do not copy \n
+       lines[lcur][slen - 1] = 0;
     }
     size_t i;
     // for (i = 0; i < lcur; i++) printf("%s\n", lines[i]);
こういうワークアラウンドでもOKなのかもしれないが(そういう点でfork/pullreqはやめた)、おそらくツールとして扱うなら改行コードがあった場合には削除...という動きが望ましいのでstrpbrk(3)やstrchr(3)と使ったり、自作strchrぽい処理を入れていくと他のsortと比較しておられる状況も少し変わったりするのかもしれませんね。確認してないけど。
sort(1)とはやってる事が違いすぎるのでそもそもな気もしなくない。どこを比較したんだろう...。

ちなみに全然オフトピだけど、GNU textutilsに入ってるsort(1)にはコア数によって動的にスレッドを生成してソートする処理が入ってるのでそういうの興味ある人はコード読むといいと思います。
Posted at by



2011/12/13


node.js の実装を司る libuv を使ってC言語のアプリケーションを書いていると、コールバック関数が増えていく。まぁこれは致し方ない事なんだけど、これで面倒なのがループを止める方法。libuv は作ったハンドルが全て閉じられると勝手に uv_run が終了する仕組みになっているんだけど、例えば処理内に2つハンドルがあって、かつ閉じようとする箇所が別のコールバック処理内にあるとハンドルをグローバル変数にするか uv_timer_t timer1;
uv_timer_t timer2;

void exit_cb(void* data) {
  uv_close(timer1);
  uv_close(timer2);
}
構造体に格納して引きずり回さなければならない。
typedef struct {
  uv_timer_t timer1;
  uv_timer_t timer2;
} THE_TIMERS;

void exit_cb(void* data) {
  THE_TIMERS* timers = (THE_TIMERS*) data;
  uv_close(timers->timer1);
  uv_close(timers->timer2);
}
どんな時に起きるかというと、libuv を GUI アプリと併用する場合。libuv はメインループを uv_run() で実行する為、GUI のメインループを回す為にアイドルを作る必要がある。

アイドルを作るだってぇ!!!

と思ったあなた、ぜひクリスマスは libuv とお過ごし下さい。
例えば、GTK だと以下の様になります。
#include <uv/uv.h>
#include <gtk/gtk.h>

void
idle_cb(uv_idle_t* idle, int status) {
  gtk_main_iteration_do(FALSE);
}

int
main(int argc, char* argv[]) {
  gtk_init(&argc, &argv);

  GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_widget_show_all(window);

  uv_idle_t idle;
  uv_idle_init(uv_default_loop(), &idle);
  uv_idle_start(&idle, idle_cb);

  uv_run(uv_default_loop());
}
この uv_run() のループを止めるには idle を uv_close() する必要があるのですが、ループ自身もハンドルで、かつ uv_run() はそのハンドルのリファレンスがある内しか回らないという仕様なので、こう書く事が出来ます。
#include <uv/uv.h>
#include <gtk/gtk.h>

void
timer_cb(uv_timer_t* timer, int status) {
  GtkWidget* label = (GtkWidget*) timer->data;
  char buf[64];
  time_t t;
  time(&t);
  strftime(buf, sizeof(buf), "%c", localtime(&t));
  gtk_label_set_text(GTK_LABEL(label), buf);
}

void
idle_cb(uv_idle_t* idle, int status) {
  gtk_main_iteration_do(FALSE);
}

void
destroy_cb(GtkWidget* w, gpointer data) {
  uv_loop_t* loop = (uv_loop_t*) data;
  uv_unref(loop); // stop idle
  uv_unref(loop); // stop timer
}

int
main(int argc, char* argv[]) {
  gtk_init(&argc, &argv);

  GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "clock");

  GtkWidget* vbox = gtk_vbox_new(FALSE, 1);
  GtkWidget* label = gtk_label_new("");
  gtk_container_add(GTK_CONTAINER(vbox), label);
  gtk_container_add(GTK_CONTAINER(window), vbox);
  gtk_window_set_default_size(GTK_WINDOW(window), 30020);
  gtk_widget_show_all(window);

  uv_timer_t timer;
  uv_timer_init(uv_default_loop(), &timer);
  timer.data = (void*) label;
  uv_timer_start(&timer, timer_cb, 10001000);

  uv_idle_t idle;
  uv_idle_init(uv_default_loop(), &idle);
  uv_idle_start(&idle, idle_cb);

  g_signal_connect(G_OBJECT(window), "destroy",
          G_CALLBACK(destroy_cb), uv_default_loop());

  uv_run(uv_default_loop());
}
もちろん、uv_close() する回数が少なければループは止まらないので、あまりちゃんとした方法では無い気もしますが。

GTKの様に iteration 動作出来るツールキットならばいいのですが、iteration 動作出来ないライブラリと併用する場合、libuv 側が回される事になる訳です。つまり libuv に1回だけループを回す手続きが必要な訳で、探しても無かったのでさっきパッチ書いて投げた。取り込まれるかどうかは知らない。
ちなみに、上記の例は libuv と gtk で作る時計なんだけど、これを perl で書くとこうなる
use strict;
use warnings;

use Encode;
use Encode::Locale;
use UV;
use Glib;
use Gtk2 -init;
use Time::Piece;

my $w = Gtk2::Window->new('toplevel');
$w->set_title("timer");
my $v = Gtk2::VBox->new(01);
my $l = Gtk2::Label->new();
$v->add($l);
$w->add($v);
$w->set_default_size(30020);
$w->show_all;

my $t = UV::timer_init();
UV::timer_start($t10001000sub {
  $l->set_label(decode(locale => localtime->strftime));
});

my $i = UV::idle_init();
UV::idle_start($isub {
  Gtk2->main_iteration_do(0);
});

$w->signal_connect(destroy => sub {
  UV::close($t);
  UV::close($i);
});

UV::run;
UV は typester さんが書いた奴。
typester/p5-UV - GitHub
https://github.com/typester/p5-UV
あと、最近書いた go言語の libuv バインディング go-uv で書くとこうなる
package main

import (
    "fmt"
    "github.com/mattn/go-uv"
    "github.com/mattn/go-gtk/gtk"
    "time"
)

func main() {
    gtk.Init(nil)
    window := gtk.Window(gtk.GTK_WINDOW_TOPLEVEL)
    window.SetTitle("Clock")
    vbox := gtk.VBox(false1)
    label := gtk.Label("")
    vbox.Add(label)
    window.Add(vbox)
    window.SetDefaultSize(30020)
    window.ShowAll()

    timer, _ := uv.TimerInit(nil)
    timer.Start(func(h *uv.Handle, status int10001000) {
        label.SetLabel(fmt.Sprintf("%v", time.Now()))
    })

    idle, _ := uv.IdleInit(nil)
    idle.Start(func(h *uv.Handle, status int) {
        gtk.MainIterationDo(false)
    })

    window.Connect("destroy"func() {
        timer.Close(nil)
        idle.Close(nil)
    })

    uv.DefaultLoop().Run()
}
mattn/go-uv - GitHub

Go binding for libuv

https://github.com/mattn/go-uv
Posted at by