2012年5月10日木曜日

unlambda in C

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
unlambdaの実装をCで書いているのだが、sをちゃんとつかうプログラムでは、objectの寿命がかなりわかりにくい。ので。GCでもすんべ、と思った。よって、valgrind投入でございます。
作業補助のやる気ないMakefile
unlambda: unlambda.c
  gcc -Wall -g -o unlambda unlambda.c

test: unlambda
  valgrind -v --error-limit=no --leak-check=yes --show-reachable=no ./unlambda 2>&1 | tee valgrind.log

DeleteObjectを削除してmakeして得た結果。ばっちり検出してくれている。
==16830== Memcheck, a memory error detector
==16830== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==16830== Using Valgrind-3.6.0 and LibVEX; rerun with -h for copyright info
==16830== Command: ./unlambda
==16830==
--16830-- Valgrind options:
--16830--    -v
--16830--    --error-limit=no
--16830--    --leak-check=yes
--16830--    --show-reachable=no
--16830-- Contents of /proc/version:
--16830--   Linux version 2.6.32-220.4.2.el6.x86_64 (mockbuild@sl6.fnal.gov) (gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC) ) #1 SMP Mon Feb 13 17:24:24 CST 2012
--16830-- Arch and hwcaps: AMD64, amd64-sse3-cx16
--16830-- Page sizes: currently 4096, max supported 4096
--16830-- Valgrind library directory: /usr/lib64/valgrind
--16830-- Reading syms from /home/nori/Desktop/study/c/unlambda/unlambda (0x400000)
--16830-- Reading syms from /usr/lib64/valgrind/memcheck-amd64-linux (0x38000000)
--16830--    object doesn't have a dynamic symbol table
--16830-- Reading syms from /lib64/ld-2.12.so (0x3135a00000)
--16830-- Reading suppressions file: /usr/lib64/valgrind/default.supp
--16830-- REDIR: 0x3135a17460 (strlen) redirected to 0x38042ae7 (vgPlain_amd64_linux_REDIR_FOR_strlen)
--16830-- Reading syms from /usr/lib64/valgrind/vgpreload_core-amd64-linux.so (0x4801000)
--16830-- Reading syms from /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so (0x4a02000)
==16830== WARNING: new redirection conflicts with existing -- ignoring it
--16830--     new: 0x3135a17460 (strlen              ) R-> 0x04a07830 strlen
--16830-- REDIR: 0x3135a172d0 (index) redirected to 0x4a07470 (index)
--16830-- REDIR: 0x3135a17350 (strcmp) redirected to 0x4a07df0 (strcmp)
--16830-- Reading syms from /lib64/libc-2.12.so (0x3135e00000)
--16830-- REDIR: 0x3135e83980 (strcasecmp) redirected to 0x4801560 (_vgnU_ifunc_wrapper)
--16830-- REDIR: 0x3135e85c40 (strncasecmp) redirected to 0x4801560 (_vgnU_ifunc_wrapper)
--16830-- REDIR: 0x3135e818f0 (__GI_strrchr) redirected to 0x4a072f0 (__GI_strrchr)
--16830-- REDIR: 0x3135e79760 (malloc) redirected to 0x4a05f10 (malloc)
Hello world
--16830-- REDIR: 0x3135e7a590 (free) redirected to 0x4a05890 (free)
==16830== 
==16830== HEAP SUMMARY:
==16830==     in use at exit: 1,000 bytes in 25 blocks
==16830==   total heap usage: 25 allocs, 0 frees, 1,000 bytes allocated
==16830== 
==16830== Searching for pointers to 25 not-freed blocks
==16830== Checked 65,664 bytes
==16830== 
==16830== 960 (440 direct, 520 indirect) bytes in 11 blocks are definitely lost in loss record 4 of 4
==16830==    at 0x4A05FDE: malloc (vg_replace_malloc.c:236)
==16830==    by 0x40058F: NewObject (unlambda.c:33)
==16830==    by 0x400701: print (unlambda.c:103)
==16830==    by 0x40098F: eval (unlambda.c:213)
==16830==    by 0x400A2D: main (unlambda.c:247)
==16830== 
==16830== LEAK SUMMARY: 
==16830==    definitely lost: 440 bytes in 11 blocks
==16830==    indirectly lost: 520 bytes in 13 blocks
==16830==      possibly lost: 0 bytes in 0 blocks
==16830==    still reachable: 40 bytes in 1 blocks
==16830==         suppressed: 0 bytes in 0 blocks
==16830== Reachable blocks (those to which a pointer was found) are not shown.
==16830== To see them, rerun with: --leak-check=full --show-reachable=yes
==16830==    
==16830== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
--16830--   
--16830-- used_suppression:      6 dl-hack3-cond-1
==16830== 
==16830== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)

2012年5月8日火曜日

方針の良し悪し・・・小さな1つの経験

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
一般的に方針の良し悪し、というと語ることは不可能なのだが、bfの処理系で得た知見をあげるなら、同じ仕様のものを作っていても、 「テストのカバレッジが異なる実装が存在しうる、ということ。

どういうことかというと:次の2つの実装をテストとしたときに

前者は多重入れ子があるときに最初問題を起こした。後者は入れ子でテストさえすれば、多重でも大丈夫なコトが分かる。

言語の処理系を利用している、と言う点でevalが有利なのは当たり前なのだが、この件はそれとはまた別の要素ではないだろうか。

残念ながら私の今までの経験ではこれをもっと一般的に語ることは不可能だ。

最近楽しんでいるもの

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

最近いじって楽しんでいるものはcloftです。

push権限ももらっていい気になっています。

cloftはclojureでかかれたbukkit pluginで、bukkitとは、minecraftのサーバです。

まだまだよくわからないことの多いclojureですが、lisp-1で関数と「変数」(正しくない書き方だろう)の名前空間が一緒なのが胡のみですね。

またカッコを減らそうという努力が伺えるのが好感度高いです。(じゃあHaskellやれよ、という声もあるのかもしれませんが・・・・)

「clojure では、マップ型および、キーワード型は、callable」で、なるほど。よく考えてある。pythonとかでやらかう[]と()をタイポしてむかつくことがないのかというのもいいです。

#!Clojure
({:a 10 :b 20 :c 30} :b)
=> 20

いままでイラつきを引き起こしている言語のくだらん記法を減らす設計に向かっているということが大事。

わかっている人に言わせればrecurとかも実用的判断らしいですが、まだよくわかりません。

専門家と議論するより、小中学生の集団を相手に会話する方が語学的にははるかに難しい。

コンテキストとスコープが共有されないと大変。 またどの範囲で効いているのかが重要で、取り違えるとまっとうな意味をなさない。

人間のコミュニケーションも、言語も一緒だよね。コンテキストを作り出す コストが高い言語は何をしても大変だろう。

話はcloftでのコードに戻る。

先に断っておくと、minecraftをやったことがないと分かりにくいと思う。

multithreadをつかってbukkitとやりとりしたらなんかおかしいのでそれを止めた

具体的にはfuture-callを使ってblockを積んだらおかしくなったのだ。

仕方ないのでJavaScriptのtimerっぽいものを実装した

大筋では

  • 起動されてからtickごとに毎回処理するcloft-scheduler
  • 何回目のtickだったか覚えるcurrent-tick
  • callbackとその発火時刻を覚えておくschedule-table
  • 「ユーザ」がcallbackの登録と発火時刻の指定に使うsettimer
からできている。

最初tableが要素にもつvectorはmutableである必要がないことに気づかなくてハマった

vectorをconsするなという噂もあります。

(def cloft-schedule-table (atom {}))
(def cloft-schedule-currenct-tick (atom 0))
(defn cloft-schedule-settimer [after f]
  ;(prn cloft-schedule-settimer after f)
  ;(prn (count @cloft-schedule-table))
  (dosync
   (let [wake-up (+ @cloft-schedule-currenct-tick after)]
     (swap! cloft-schedule-table assoc wake-up
            (cons f (@cloft-schedule-table wake-up []))))))

(defn cloft-scheduler []
  (dosync
    (let [table @cloft-schedule-table
          now @cloft-schedule-currenct-tick
          r (table now false)]
      (when r
        ;(prn r table now)
        (doseq [f r] (f)))
      (swap! cloft-schedule-table dissoc @cloft-schedule-currenct-tick))
    (swap! cloft-schedule-currenct-tick inc)))

これをplugin起動時に登録する。サーバ側は1tickごとにcloft-schedulerを呼び出す。ちなみに1tickは50msです。

  (.scheduleSyncRepeatingTask (Bukkit/getScheduler) plugin (fn [] (cloft-scheduler)) 0 1)

2012年5月4日金曜日

bf処理系、Cで構文木(?)作るバージョン

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

bf2c.pyでトランスレータはおもしろくないことが分かっていたので、解析してtreeを作らせた。ほぼlispのcons対。

include 
#include 
#include 


/*
 *  http://en.wikipedia.org/wiki/Brainfuck
 */

char* helloworld_expected = "Hello World!\n";
char* helloworld = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.";


/* 
 *  http://codegolf.com/competition/run/8/2
 */

char* another_expected = "Just another brainfuck hacker.";
char* another = 
  "+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<"
  ".<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+"
  "+.++>+++[.<][.]<++.";


char memory[30000];
char *data = memory;
char *tape= 0;

typedef struct TAst Ast;

union UValue {
  char uChar;
  Ast *uAst;
};

typedef union UValue Value;

struct TAst {
  Ast* fNext;
  int fType;
  Value fValue;
};


int debug = 0;

Ast* make_node(Ast* parent){
  Ast* ast;
  ast = malloc(sizeof(Ast));
  if(parent){
    parent->fNext = ast;
  }

  if(debug)
    printf("makde ast node %p (parent %p)\n", ast, parent);//->fValue.uAst);
  return ast;
}

Ast* parse(char** p){
  Ast* root;
  Ast* head;
  Ast* n;
  char* sub;

  root = make_node(0);
  root->fNext = 0;
  root->fType = 0;
  head = root;

  while (**p){
    switch(**p){
      case '[':
        n = make_node(head);
        (*p)++; //consume [
        n->fNext = 0;
        n->fType = 0;
        n->fValue.uAst = parse(p);
        head = n;
        break;
      case ']': 
        // ] will be consumed after return, in '[' case.
        return root->fNext;
      default:
        n = make_node(head);
        n->fNext = 0;
        n->fType = 1;
        n->fValue.uChar = **p;
        head = n;
        break;
    }
    (*p)++;
  }
  return root->fNext;
}

void print_ast(Ast *ast, int indent){
  int i;
  
  while(ast){
    if (ast->fType){
      for(i=0; i< indent; i++){
        printf(" ");
      }
      printf("%c\n", ast->fValue.uChar);
    }else{
      for(i=0; i< indent; i++){
        printf(" ");
      }
      printf("Entered %p \n", ast->fValue.uAst);
      print_ast(ast->fValue.uAst, indent+2);
    }
    ast = ast->fNext;
  }
}


void run(Ast *ast){
  while (ast){
    if(debug)
      printf("%i, %i, %i, %i, %i\n", memory[0], memory[1], memory[2], memory[3], memory[4]);
    if (ast->fType){
      if(debug)
        printf("processing %c\n", ast->fValue.uChar);
      switch (ast->fValue.uChar){
        case '>':
          /* increment the data pointer (to point to the next cell to the right). */
          data++;
          break;
    
        case '<':
          /* decrement the data pointer (to point to the next cell to the left). */
          data--;
          break;
    
        case '+':
          /* increment (increase by one) the byte at the data pointer. */
          (*data)+=1;
          break;
    
        case '-':
          /* decrement (decrease by one) the byte at the data pointer. */
          (*data)-=1;
          break;
          
        case '.':
          /* output the byte at the data pointer as an ASCII encoded character.*/
          if(debug)
            printf("%i", *data);
          printf("%c", *data);
          break;
    
        case ',':
          /* accept one byte of input, storing its value in the byte at the data pointer. */
          /* not implemented */
          break;
        default:
          break;
      }
    }else{
      if(debug)
        printf("handling subtree, %p\n", ast->fValue.uAst);
      /* subtree from []*/
      /* If the byte at the data pointer is zero, jump it forward to the command after the matching ] command*. */
      /* if the byte at the data pointer is nonzero, jump it back to the command after the matching [ command*. */
      while(*data){
        run(ast->fValue.uAst);
      }
    }
    ast = ast->fNext;
  };
}


int main(arg, argv){
  Ast* ast;
  char* tape;
  //tape = helloworld;
  tape = another;
  ast = parse(&tape);
  if(debug){
    printf("============================\n");
    print_ast(ast,0);
    printf("============================\n");
  }

  run(ast);
  return 0;
}
     

bf処理系、インタプリタなC言語バージョン

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

時系列的にはpythonのあと、rubyの前に書いている

include 
#include 
#include 


/*
 *  http://en.wikipedia.org/wiki/Brainfuck
 */

char* helloworld = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.";
char* helloworld_expected = "Hello World!\n";


/* 
 *  http://codegolf.com/competition/run/8/2
 */

char* another = 
  "+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<"
  ".<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+"
  "+.++>+++[.<][.]<++.";
char* another_expected = "Just another brainfuck hacker.";


char memory[30000];
char *data = memory;
char *tape= 0;

char* bf(char* p, int run){
  char* r;
  char* start;

  //printf("%p, %i\n", p, run);

  while (*p){
    //printf("%i, %i, %i, %i, %i\n", memory[0], memory[1], memory[2], memory[3], memory[4]);
    //printf("%s\n", p);
    switch (*p){
      case '>':
        /* increment the data pointer (to point to the next cell to the right). */
        if(run){
          data++;
        }
        break;

      case '<':
        /* decrement the data pointer (to point to the next cell to the left). */
        if(run){
          data--; }
        break;

      case '+':
        /* increment (increase by one) the byte at the data pointer. */
        if(run){
          (*data)+=1;
        }
        break;

      case '-':
        /* decrement (decrease by one) the byte at the data pointer. */
        if(run){
          (*data)-=1;
        }
        break;
        
      case '.':
        /* output the byte at the data pointer as an ASCII encoded character.*/
        if(run){
          printf("%c", *data);
        }
        break;

      case ',':
        /* accept one byte of input, storing its value in the byte at the data pointer. */
        /* not implemented */
        if(run){
        }
        break;

      case '[':
        /* if the byte at the data pointer is zero, jump it forward to the command after the matching ] command*. */
        if (*data && run){
          start = p;
          assert(*start=='[');
          r = bf(p+1, 1);
          if(!r){
            r = start - 1; /* rewind, ajust against p++ */
          }
        }else{
          r = bf(p+1, 0); /* skip to ] */
          //printf("skipped to '%s'\n", r);
        }
        p = r;
        break;

      case ']':
        /* if the byte at the data pointer is nonzero, jump it back to the command after the matching [ command*.*/
        /* ==> */
        /* if the byte at the data pointer is zero, moving the instruction pointer forward */
        /* ==> */
        /* just rewind */
        if(run){
          return (char*)0;
        }else{
          return p;
        }
      default:
        break;
    };
    p++;
  };
  return (char*)0;
}


int main(arg, argv){
  //bf(helloworld, 1);
  bf(another, 1);
  return 0;
}
    

bf2c.py

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

これはpythonで書いたbfからCへのトランスレータ。wikipediaの解説、そのまま。

import sys

fname = sys.argv[1]

f = open(fname)
source = f.read()
f.close()

header = """
#include

int main(int argc, char**argv){
  char array[30000];
  char *ptr=array;

"""

footer = """
  return 0;
}
"""
mapping = {
  '>':  "++ptr;",
  '<':  "--ptr;",
  '+':  "++*ptr;",
  '-':  "--*ptr;",
  '.':  "putchar(*ptr);",
  ',':  "*ptr=getchar();",
  '[':  "while (*ptr) {",
  ']':  "}",
}

outname = fname[:-2]+'c'

f = open(outname, 'w')
f.write(header)

for c in source:
  o = mapping.get(c, '');
  f.write(o +'\n')

f.write(footer)
f.close()
    

bf処理系、pythonでevalバージョン

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
標記のバージョン。だいぶさっぱりしている。bfからpythonへのトランスレータなんですよね。 どの言語で書いてもトランスレータであるほうが楽。もっともbfからCとかだとgccが必要ですが。
import sys


source = sys.stdin.read()  

data = [0 for i in range(30000)]
L = {     
  "data" : data,
  "stdout" : sys.stdout,
  "idx" :0,
}

G ={}

mapping = {
  '>':  "idx+=1",
  '<':  "idx-=1",
  '+':  "data[idx]+=1",
  '-':  "data[idx]-=1",
  '.':  "stdout.write(chr(data[idx]))",
  ',':  " #not implemented ",
  '[':  "while data[idx]:",
  ']':  "False",
}


py = []
indent = ''
for c in source:
  o = mapping.get(c, '');
  py.append(indent + o + ' # ' + c +'\n')
  if c=='[':
    indent += '  '
  elif c == ']':
    indent = indent[:-2]
  else:
    pass
  #if c=='[':
  #  py.append(indent + 'print idx' + '\n')

p = ''.join(py)

#print len(data)
#for line, k in enumerate(p.split('\n')):
#  print '%4d: %s'%(line, k)
exec p in G, L
    

bf処理系 (python non-eval)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
んで、文字列のリスト化を踏まえてbrainf**kの処理系をかけとのお題が...

pythonを用いたnon-eval version。結構苦戦した。「ブロック」の扱いを正しく理解していなかった。

from sys import stdout


debug = True
class BF:
  '''
  see http://en.wikipedia.org/wiki/Brainfuck
  >>> tape = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
  >>> bf = BF(tape)
  >>> bf.run()
  Hello,World!

  >>> 
  '''
  ops = {
    '>': 'incp',
    '<': 'decp',
    '+': 'incd',
    '-': 'decd',
    '.': 'putc',
    ',': 'readc',
    '[': 'blockstart',
    ']': 'blockend',
  }
  
  def __init__(self, xs, data=None, ptr=None, dryrun=None, parent=None):
    self.parent = parent
    self.terminate = False
    self.orig = str(xs)
    self.xs = str(xs)
    if dryrun is None or not dryrun :
      self.dryrun=False
    else:
      self.dryrun=True
    if debug:
      print self, self.parent, self.dryrun

    if ptr is None:
      self.ptr = 0
    else:
      self.ptr = ptr
    if data is None:
      # assuming ptr is of type unsigned char* and has been initialized to point
      # to an array of zeroed bytes:
      self.data = [0 for i in range(30000)]
    else:
      self.data = data
    self.sub = None

  def run(self):
    while self.xs and not self.terminate:
      x = self.xs[0]
      self.xs = self.xs[1:]
      if debug:
        print self.ptr, self.data[:5], self.xs
        print x, self.dryrun, self.parent
      name = self.ops[x]
      if self.dryrun:
        if name not in ("blockstart",  "blockend"):
          name = 'null'
      handler = getattr(self, 'handle_' + name )
      handler() 
      
  def handle_null(self):
    pass
    
  def handle_incp(self):
    '>'
    self.ptr += 1
    
  def handle_decp(self):
    '<'
    self.ptr -= 1
    
  def handle_incd(self):
    '+'
    self.data[self.ptr] += 1
    
  def handle_decd(self):
    '-'
    self.data[self.ptr] -= 1
    
  def handle_putc(self):
    '.'
    #print self.data[self.ptr]
    stdout.write(chr(self.data[self.ptr]))
    stdout.flush()
    
  def handle_readc(self):
    ','
    # not used in Hello,World!
    pass
    
  def handle_blockstart(self):
    '['
    '''
      if the byte at the data pointer is zero, then instead of moving the
      instruction pointer forward to the next command, jump it forward to the
      command after the matching ] command*.
    '''
    if self.data[self.ptr] and not self.dryrun:
      # go into block
      dryrun = False 
    else:
      # skip until matching ']'
      dryrun = True 

    self.sub = BF(self.xs, self.data, self.ptr, dryrun, self)
    self.sub.run()
    if debug:
      print 'leaving', self.sub
   
    #write back child state
    self.data = self.sub.data #not needed. 
    self.ptr = self.sub.ptr
    self.xs = self.sub.xs

  def handle_blockend(self):
    '''
      if the byte at the data pointer is nonzero, then instead of moving the
      instruction pointer forward to the next command, jump it back to the command
      after the matching [ command*.
    '''
    ']'
    if self.data[self.ptr] and not self.dryrun:
      #jump back by rewind
      self.xs = '[' + self.orig 
    else:
      #step out 
      pass
    self.terminate = True


if __name__ == "__main__":
  #import doctest
  #doctest.testmod()
  #tape = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
  #tape = "[][]" 
  tape ='''+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<'''\
      + '''.<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+'''\
      + '''+.++>+++[.<][.]<++.'''
  #tape = "[[]]" 
  print tape
  debug =False


  bf = BF(tape)
  bf.run()
    

文字列のリスト化、ruby編

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
ruby編。pythonよりスパッと解けているのは問題をより把握したため。

eval使うバージョン

s = "ab[cd]ef"
s = "ab[c[d]][e]f"
#expected = ['a', 'b', ['c', 'd' ], 'e', 'f']


s = s.gsub(/([\]a-z])/, '\1,')
s = s.gsub(/([a-z])/, '"\1"')
s = s.gsub(/,\]/, ']')
#puts s


p eval('[' + s +']')

    

eval使わないバージョン

s = "ab[cd]ef"
expected = ['a', 'b', ['c', 'd' ], 'e', 'f']




def parse(xs)
  result = []

  while not xs.empty?
    x = xs[0]
    xs.slice!(0, 1)
    if x == '['
      got = parse(xs)
      result.push(got)
    elsif x == ']'
      return result
    else
      result.push(x)
    end
  end
  return result
end


p parse("[][]")
p parse(s)

    

文字列のリスト化回答編(python)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

非evalバージョン

class Hoge:
  def __init__(self, xs):                                        
    self.xs = xs                                                 
    self.result = []                                             
    self.sub = None                                              
  
  def run(self):                                                 
    if not self.xs: 
      return self.result
    while self.xs:
      x = self.xs[0]
      self.xs = self.xs[1:]
      if x == '(':                                               
        self.sub = Hoge(self.xs)                                 
        self.sub.run()                                           
        self.result.append(self.sub.result)                      
        self.xs = self.sub.xs                                    
      elif x == ')':                                             
        return                                                   
      else:
        self.result.append(x)                                    
  
def f(x):                                                        
  h = Hoge(x)                                                    
  h.run()                                                        
  return h.result                                                
  
                                                                 
def test0():                                                     
  return f('') == []
                                                                 
def test1():                                                     
  return f('a') == ['a']                                         
                                                                 
def test2():                                                     
  return f('()') == [[]]
                                                                 
def test3():                                                     
  return f('(()())') == [[[],[]]]
                                                                 
def testX():                                                     
  return f("ab(cd)ef") == ['a', 'b', ['c', 'd'], 'e', 'f']       

print test0()                                                    
print test1()                                                    
print test2()
print test3()
print testX()
    

evalバージョン

import re

r = re.compile('a-zA-Z')

def f(xs):
  #'a' => 'a,'
  #'(' => '['
  #')' => ']'
  p = xs
  p = re.sub(r'\(', r"[", p)
  p = re.sub(r'\)', r"]", p)
  p = re.sub(r'([a-zA-Z])', r"'\1',", p)
  p = re.sub(r"\]'([a-zA-Z])", r"], '\1", p)
  p = re.sub(r'\]\[', r"],[", p)
  
  if not p:
    p = ' '
  p = '[' + p + ']'
  print p
  x = eval(p)
  print x
  y = list(x)
  print y
  return y



def test0():
  return f('') == []

def test1():
  return f('a') == ['a']

def test2():
  return f('()') == [[]]

def test3():
  return f('(()())') == [[[],[]]]

def testX():
  return f("ab(cd)ef") == ['a', 'b', ['c', 'd'], 'e', 'f']


print test0()
print test1()
print test2()
print test3()
print testX()
    
このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

文字列を回答に使用する言語のリストに変換せよ

ただしカッコがマッチしているコトを仮定してよい

  1. a) eval等を利用するもの
  2. b) eval等を利用しないもの

例: "ab(cd)ef" == ['a', 'b', ['c', 'd'], 'e', 'f']

...というお題を師匠からいただき、解くことに。

pythonでnon-eval versionを書くのに50分近くかかったorz.