Blogged by Ujihisa. Standard methods of programming and thoughts including Clojure, Vim, LLVM, Haskell, Ruby and Mathematics written by a Japanese programmer. github/ujihisa

Tuesday, May 12, 2009

Read parse.y and learn about a colon

In Ruby, there are a lot of usages of colon.

  • Symbol :aaa
  • Symbol with single quotes :'aaa'
  • Symbol with double quotes :"aaa"
  • Conditional operator aaa ? bbb : ccc
  • Class hierarchy A::B

The following is an excerpt from parse.y relates a colon:

case ':':
  c = nextc();
  if (c == ':') {
      if (IS_BEG() ||
          lex_state == EXPR_CLASS || (IS_ARG() && space_seen)) {
          lex_state = EXPR_BEG;
          return tCOLON3;
      }
      lex_state = EXPR_DOT;
      return tCOLON2;
  }
  if (lex_state == EXPR_END || lex_state == EXPR_ENDARG || (c != -1 && ISSPACE(c))) {
      pushback(c);
      lex_state = EXPR_BEG;
      return ':';
  }
  switch (c) {
    case '\'':
      lex_strterm = NEW_STRTERM(str_ssym, c, 0);
      break;
    case '"':
      lex_strterm = NEW_STRTERM(str_dsym, c, 0);
      break;
    default:
      pushback(c);
      break;
  }
  lex_state = EXPR_FNAME;
  return tSYMBEG;

If the next character of ':' is ':', in a nutshell if '::' has come, it returns tCOLON3 (:: at the very left) or tCOLON2 (:: at the right hand). Otherwise if the colon is just after expressions, it's a conditional operator. Ditto if the next character is a space. Otherwise finally the colon is a prefix of symbol.

I've never known that we can have a newline after ::!

class A::

B
end

It's valid.

OK... So, let's try to add a new literal :-) which has equivalent to =>.

diff --git a/parse.y b/parse.y
index e2e92ce..8e49bf4 100644
--- a/parse.y
+++ b/parse.y
@@ -7082,6 +7082,9 @@ parser_yylex(struct parser_params *parser)

       case ':':
         c = nextc();
+        if (c == '-' && nextc() == ')') {
+            return tASSOC;
+        }
         if (c == ':') {
             if (IS_BEG() ||
                 lex_state == EXPR_CLASS || (IS_ARG() && space_seen)) {

That's easy.

$ ./ruby -e 'p({ 1 :-) 2 })'
{1=>2}

cool!

Monday, May 11, 2009

Save the iTerm

System Preferences

Now you don't have to care about typing Cmd-Q or Cmd-W by mistake.

Thursday, May 7, 2009

About Array#<=> (a.k.a. Spacecraft Operator)

The documentations about Array#<=> are:

For example:

[1, 2, 3, 4] <=> [1, 2, 3, 3] #=> 1
[1, 2, 3, 4] <=> [1, 2, 3, 4] #=> 0
[1, 2, 3, 4] <=> [1, 2, 3, 5] #=> -1
[1, 2, 3, 4] <=> [1, 2, 3] #=> 1

The implementation in MRI 1.9 is:

VALUE
rb_ary_cmp(VALUE ary1, VALUE ary2)
{
    long len;
    VALUE v;

    ary2 = to_ary(ary2);
    if (ary1 == ary2) return INT2FIX(0);
    v = rb_exec_recursive(recursive_cmp, ary1, ary2);
    if (v != Qundef) return v;
    len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
    if (len == 0) return INT2FIX(0);
    if (len > 0) return INT2FIX(1);
    return INT2FIX(-1);
}

static VALUE
recursive_cmp(VALUE ary1, VALUE ary2, int recur)
{
    long i, len;

    if (recur) return Qnil;
    len = RARRAY_LEN(ary1);
    if (len > RARRAY_LEN(ary2)) {
        len = RARRAY_LEN(ary2);
    }
    for (i=0; i<len; i++) {
        VALUE v = rb_funcall(rb_ary_elt(ary1, i), id_cmp, 1, rb_ary_elt(ary2, i));
        if (v != INT2FIX(0)) {
            return v;
        }
    }
    return Qundef;
}

I translated it from C to Ruby literally:

class Array
  def yet_another_cmp(you)
    you = you.to_ary
    return 0 if self == you
    v = nil
    (0...[self.length, you.length].min).each do |i|
      v = self[i] <=> you[i]
      return v if v != 0
    end
    len = self.length - you.length
    return 0 if len == 0
    return 1 if len > 0
    -1
  end
end

It works exactly the same.

You may think it is easy to write a simpler equivalent implement with Array#zip, but unfortunately I found that it was not so simple. The following is the simplest code I can write. Of course it works exactly the same as original <=>.

class Array
  def simple_cmp(you)
    self.zip(you) {|x, y|
      break if (x && y).nil?
      (v = x <=> y) == 0 or return v
    }
    self.length <=> you.length
  end
end

In conclusion, Array#<=> itself is complicated one. Enjoy your space travel!

Saturday, May 2, 2009

A benchmark of Array#to_s

A bugfix patch may make Array#to_s slow. I am finding out how slow it becomes.

That patch changes the definition of Array#to_s from:

rb_define_method(rb_cArray, "to_s", rb_ary_inspect, 0);
rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0);

to:

rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0);
rb_define_alias(rb_cArray,  "to_s", "inspect");

This change effects only in the array has itself recursively.

Benchmark code I use is:

require 'benchmark'
short_a = Array.new(100) { rand }
long_a = Array.new(10000) { rand }

Benchmark.bmbm do |b|
  b.report do
    1000.times do
      short_a.to_s
    end
  end

  b.report do
    10.times do
      long_a.to_s
    end
  end
end

And the results are below.

Conventional (before the patch applied)

Rehearsal ------------------------------------
   0.810000   0.010000   0.820000 (  0.836625)
   0.870000   0.010000   0.880000 (  0.901661)
--------------------------- total: 1.700000sec

       user     system      total        real
   0.740000   0.010000   0.750000 (  0.748501)
   0.770000   0.010000   0.780000 (  0.801869)

Current (after the patch applied)

Rehearsal ------------------------------------
   0.820000   0.000000   0.820000 (  0.852360)
   0.860000   0.010000   0.870000 (  0.897602)
--------------------------- total: 1.690000sec

       user     system      total        real
   0.850000   0.010000   0.860000 (  0.873125)
   0.830000   0.010000   0.840000 (  0.871903)

There are certainly slow changes, but they seem enough slight. What do you think about it?

Thursday, April 30, 2009

Termtter Skype Public Chat Room

Developpers of a cool twitter client termtter have their own chat room the termtter lingr room. Unfortunatelly, Lingr is going to get closed. Accordingly I created the Termtter Skype Public Chat Room.

http://www.skype.com/go/joinpublicchat?skypename=ujihisa23&topic=termtter&blob=Rngw4xGf8Be5Ss0oxpe_wEHXO0SpFrAe4YOH1GwGYHtU84wInDRSQCxw4wePdYVZ

If you cannot join this room, feel free to contact me.

Sunday, April 26, 2009

git-rm completion

I used this opportunity to add git-rm completion for zsh. Apply the following patch:

--- /usr/share/zsh/4.3.4/functions/_git.orig    2009-04-26 15:12:57.000000000 -0700
+++ /usr/share/zsh/4.3.4/functions/_git 2009-04-26 15:12:37.000000000 -0700
@@ -1,4 +1,4 @@
-#compdef git git-apply git-checkout-index git-commit-tree git-hash-object git-index-pack git-init-db git-merge-index git-mktag git-pack-objects git-prune-packed git-read-tree git-unpack-objects git-update-index git-write-tree git-cat-file git-diff-index git-diff-files git-diff-stages git-diff-tree git-fsck-objects git-ls-files git-ls-tree git-merge-base git-name-rev git-rev-list git-show-index git-tar-tree git-unpack-file git-var git-verify-pack git-clone-pack git-fetch-pack git-http-fetch git-local-fetch git-peek-remote git-receive-pack git-send-pack git-ssh-fetch git-ssh-upload git-update-server-info git-upload-pack git-add git-am git-applymbox git-bisect git-branch git-checkout git-cherry-pick git-clone git-commit git-diff git-fetch git-format-patch git-grep git-log git-ls-remote git-merge git-mv git-octopus git-pull git-push git-rebase git-repack git-reset git-resolve git-revert git-shortlog git-show-branch git-status git-verify-tag git-whatchanged git-applypatch git-archimport git-convert-objects git-cvsimport git-lost-found git-merge-one-file git-prune git-relink git-svnimport git-symbolic-ref git-tag git-update-ref git-check-ref-format git-cherry git-count-objects git-daemon git-get-tar-commit-id git-mailinfo git-mailsplit git-patch-id git-request-pull git-send-email git-stripspace
+#compdef git git-apply git-checkout-index git-commit-tree git-hash-object git-index-pack git-init-db git-merge-index git-mktag git-pack-objects git-prune-packed git-read-tree git-unpack-objects git-update-index git-write-tree git-cat-file git-diff-index git-diff-files git-diff-stages git-diff-tree git-fsck-objects git-ls-files git-ls-tree git-merge-base git-name-rev git-rev-list git-show-index git-tar-tree git-unpack-file git-var git-verify-pack git-clone-pack git-fetch-pack git-http-fetch git-local-fetch git-peek-remote git-receive-pack git-send-pack git-ssh-fetch git-ssh-upload git-update-server-info git-upload-pack git-add git-rm git-am git-applymbox git-bisect git-branch git-checkout git-cherry-pick git-clone git-commit git-diff git-fetch git-format-patch git-grep git-log git-ls-remote git-merge git-mv git-octopus git-pull git-push git-rebase git-repack git-reset git-resolve git-revert git-shortlog git-show-branch git-status git-verify-tag git-whatchanged git-applypatch git-archimport git-convert-objects git-cvsimport git-lost-found git-merge-one-file git-prune git-relink git-svnimport git-symbolic-ref git-tag git-update-ref git-check-ref-format git-cherry git-count-objects git-daemon git-get-tar-commit-id git-mailinfo git-mailsplit git-patch-id git-request-pull git-send-email git-stripspace

 # Commands not completed:
 # git-sh-setup
@@ -630,6 +630,16 @@
     '*:file:_files -g "*(^e:__git_is_indexed:)"' && ret=0
 }

+_git-rm () {
+  _arguments \
+    '-n[do not actually remove files; only show which ones would be removed]' \
+    '(-q --quiet)'{-q,--quiet}'[operate quietly]' \
+    '--cached[do not consider the work tree at all]' \
+    '-f[force removing even if targets exist]' \
+    '-r[recurse into subdirectories]' \
+    '*:file:_files -g "*(^e:__git_is_indexed:)"' && ret=0
+}
+
 _git-am () {
   _arguments \
     '--3way[use 3-way merge if patch does not apply cleanly]' \

enjoy your git-rm life!

Followers