2005 6 7 8 9 10 11 12
2006 1 2 3 4 5 6 7 8 9 10 11 12
2007 1 2 3 4 5 6 7 8 9 10 11 12
2008 1 2 3 4 5 6 7 8 9 10 11 12
2009 1 2 3 4 5 6 7 8 9 10 11 12
2010 1 2 3 4 5 6 7 8 9 10 11 12
2011 1 2 3 4 5 6 7 8 9 10 11 12
2012 1 2 3 4 5 6 7 8 9 10 11 12
2013 1 2 3 4 5 6 7 8 9 10 11 12
2014 1 2 3 4 5 6 7 8 9 10 11 12
2015 1 2 3 4 5 6 7 8 9 10 11 12
2016 1 2 3 4 5 6 7 8 9 10 11 12
2017 1 2 3 4 5 6 7 8 9 10

ホーム

2010年01月27日

うわ、大失態。

jrubyで『JavaのクラスのスーパークラスとしてRubyの
サブクラスを作ったときに、Javaクラスのコンストラクタが
呼び出されない』って書いたけど、真っ赤なウソでした。
申し訳ありませんでした。

つーか、オレがRubyのことをよくわかってなかった。

class Parent
  def initialize
    p :parent
  end
end

class Child < Parent
  def initialize
    p :child
  end
end

Child.new

このとき、出力は:

:child

となって、これはRuby的にも、jruby的にもまったく
正しい動作。

Rubyでは、スーパークラスのコンストラクタは明示的に
呼ばないといけない:

class Parent
  def initialize
    p :parent
  end
end

class Child < Parent
  def initialize
    super()
    p :child
  end
end

Child.new

この出力は:

:parent
:child

となる。

サーセンっした。

本家Permlink


2010年01月26日2

わかりやすい例で書くと:

@x = [1,2,3]
@y = [1,2,3]

みたいなコードをよく見るんだよね。こんなのは:

class Point
  def initialize(x, y)
    @x = x
    @y = y
  end
  attr_accessor :x, :y
end

@points = [Point.new[1, 1], Point.new[2, 2], Point.new[3, 3]]

みたいにくくり出す。

PGみたくリスト至上主義だとしても:

@points = [[1, 1], [2, 2], [3, 3]]

みたいに書く。

ひとつの塊で表現されるべきものはひとつの塊として
表現する。それなのにバラバラにしてたら:

@x.size.times do |i|
  p [@x[i], @y[i]]
end

みたいなコードを書くハメになる。まず、2つの配列が
必ず同じ要素数を持つという暗黙の前提がある点が気に
入らない。次に、配列のインデックスを何回も使わないと
いけない点が気に入らない。

ひとつの塊にまとめておけば:

@points.size.times do |i|
  point = @points[i]
  p [point.x, point.y]
end

ちょっとこれはヤラセっぽくって、モダンな言語なら
イテレータを使って、インデックスを使わずに済ませる:

@points.each do |point|
  p [point.x, point.y]
end

こういうのはオブジェクト指向以前の話で。C言語なら
構造体があるし、古典的なPascalにもレコード型がある。

--

と、ここまで書いて、C言語の構造体とか、Pascalの
レコード型とかを使った設計のことを何と呼ぶかWikipediaで
調べてみたんだけど、どうもはっきりしない。

例によって日本語ページは貧弱すぎて話にならない。

で、英語ページだと、構造化プログラミングには主に
2つあって、ひとつはダイクストラのヤツで、もうひとつは
ジャクソンのヤツ。

http://en.wikipedia.org/wiki/Structured_programming

ダイクストラのヤツは、例のgoto害悪論が出てくる話。
これは構造体とは直接は関係なさげ。

で、ジャクソンのほうは、データの話は出てくるんだけど、
これまた構造体の話とは直接関係なさげ。こっちは、
データの流れに沿ってプログラムの構造を決めましょう
っていう話。ぶっちゃけ、よくわかんない。

そもそも、ここで構造体と呼んでるものの学術的な
名称がはっきりしない。それを調べてみると、レコード
というのが一番通りがいいようだ:

http://en.wikipedia.org/wiki/Record_%28computer_science%29

でも、レコードと設計を結びつけるような話は出てこない。

個人的にはレコードからADTへ、そしてOOPへっていうのが
スッキリした流れだと思うんだけど、歴史はもっとモヤモヤと
したものなのかもしれない。

本家Permlink


2010年01月26日1

「東洋経済 2010/1/30号」、「知の技法、出世の作法」
(佐藤優)より:

  繰り返しになるが、数学は頭だけでは理解できない。
  語学同様に体で覚える技法(テクネー)の性格がある
  からだ。覚え込むためにはノートとペン(もしくは
  鉛筆、シャーペン)が必要だ。

Radiumさんのページ:

http://d.hatena.ne.jp/KZR/20080808/p1

に限ったことではなく、最近ひとつの単位として
一万時間というのを耳にする。

10,000時間とはどのくらいの時間だろうか。

1日8時間そのことに携わるとして1250日。

そのうち、1年で250日そのことに携わるとして5年。

実際は1日5時間くらいしか携われないかもしれない。
同様に1年が250日だとすれば8年という時間になる。

この計算でもわかるように、10,000時間というのは、
詰められる数字でもある。もし不眠不休で一心不乱に
やったとしたら1年ちょっとで達成できてしまう。

もちろん、健康を害してしまっては元も子もない。
しかし、詰められる数字であることに変わりはない。

本家Permlink


2010年01月26日

例によってSwing Tutorialをjrubyに移してたんですけど、
Javaのほうでメソッドが多重定義されていることに
気づかなくって、延々悩んでました。

もともと、多重定義、好きじゃないんですよね。名前
変えたほうが無難なことが多い気がするんです。

ま、Haskellとかが流行ったらそうもいってられなく
なるのかな。

本家Permlink


2010年01月23日1

java.util.Collections#binarySearchって面白いメソッド
ですね。

Listの中にkeyが見つかったら、そのインデックスを返す
っていうのは、よくある検索メソッドと同じなんですけど。
keyが見つからなかったときは、そのkeyをListに挿入したら
何番目になるかを返します。この説明はかなり不正確で、
正しくは:

http://java.sun.com/javase/7/docs/api/java/util/Collections.html#binarySearch%28java.util.List,%20T%29

で、サンプル:

import java.util.*;

class Jv {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(&quot;b&quot;);
        list.add(&quot;c&quot;);
        list.add(&quot;d&quot;);
        int ret = Collections.binarySearch(list, &quot;c&quot;);
        System.out.println(ret);
        ret = Collections.binarySearch(list, &quot;a&quot;);
        System.out.println(ret);
        ret = Collections.binarySearch(list, &quot;e&quot;);
        System.out.println(ret);
    }
}

出力:

1
-1
-4

--

で、Swing TutorialでそのbinarySearchを使ってるとこが
あるんだけど、どうしようか悩んだんですよね。でも、
試してみたら何のことはない、jrubyのArrayはListを
implementsしてて、Collections#binarySearchにそのまま
渡せたという:

include Java
java_import java.util.Collections

array = ["b", "c", "d"]
p Collections.binarySearch(array, "c")
p Collections.binarySearch(array, "a")
p Collections.binarySearch(array, "e")

出力:

1
-1
-4

本家Permlink


2010年01月23日

include Java

java_import java.awt.Color
java_import javax.swing.JFrame

class ElevatorButtonTest
  include java.awt.event.ItemListener
  include java.lang.Runnable

  def initialize
    @floorButtons = []
    @selectAllButton = nil
    @floorStatuses ={}
  end

  def setContentPane(frame)
    contentPane = javax.swing.JPanel.new
    contentPane.setOpaque(true)
    addFloorButtons(contentPane)
    addSelectAllButton(contentPane)
    frame.setContentPane(contentPane)
  end

  def addFloorButtons(contentPane)
    (1..5).each do |i|
      @floorButtons << addFloorButton(contentPane, i)
    end
  end

  def addFloorButton(contentPane, floor)
    return addButton(contentPane, floor.to_s)
  end

  def addButton(contentPane, title)
    button = javax.swing.JCheckBox.new(title)
    button.addItemListener(self)
    contentPane.add(button)
    return button
  end

  def addSelectAllButton(contentPane)
    @selectAllButton = addButton(contentPane, "Select All")
  end

  def show
    frame = JFrame.new("Elevator")
    frame.setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE)
    setContentPane(frame)
    frame.pack
    frame.setVisible(true)
  end

  def run
    show
  end

  def itemStateChanged(event)
    button = event.getSource
    if button == @selectAllButton
      doSelectAllAction(event)
    end
  end

  def doSelectAllAction(event)
    selected = (event.getStateChange == java.awt.event.ItemEvent::SELECTED)
    if selected
      saveFloorStatuses
      @floorButtons.each{|button| button.setSelected(selected)}
    else
      restoreFloorStatuses
    end
  end

  def saveFloorStatuses
    @floorButtons.each do |button|
      @floorStatuses[button] = button.isSelected
    end
  end

  def restoreFloorStatuses
    @floorStatuses.each do |button, selected|
      button.setSelected(selected)
    end
  end
end

javax.swing.SwingUtilities.invokeLater(ElevatorButtonTest.new)

本家Permlink


2010年01月18日

jrubyなんですけど、やっぱり、Javaのクラスから
Rubyのサブクラス作るときに、コンストラクタを
オーバーライドできないのは、致命的な欠陥ですよね。

たとえば、こんなコードが意図したとおりに動かない:

class TabButton < JButton
  include MouseListener

  def initialize
    size = 17
    setPreferredSize(Dimension.new(size, size))
    setToolTipText("close this tab")
    setUI(BasicButtonUI.new)
    setContentAreaFilled(false)
    setFocusable(false)
    setBorder(BorderFactory.createEtchedBorder)
    setBorderPainted(false)
    addMouseListener(self)
    setRolloverEnabled(true)
  end
  ...

仕方ないから:


class TabButton < JButton
  include MouseListener

  def init
    size = 17
    setPreferredSize(Dimension.new(size, size))
    setToolTipText("close this tab")
    setUI(BasicButtonUI.new)
    setContentAreaFilled(false)
    setFocusable(false)
    setBorder(BorderFactory.createEtchedBorder)
    setBorderPainted(false)
    addMouseListener(self)
    setRolloverEnabled(true)
  end
  ...

みたいに初期化用のメソッドを用意して:

button = TabButton.new
button.init

みたいに書くしかない。

本家Permlink


2010年01月16日

う〜む:

~/jruby$ jirb
irb(main):001:0> fmt = java.text.DateFormatSymbols.new
=> #<Java::JavaText::DateFormatSymbols:0xadf5be>
irb(main):002:0> months = fmt.getMonths
=> #<#<Class:01x10d95cd>:0xe5f46e>
irb(main):003:0> months[-1]
ArgumentError: index out of bounds for java array (-1 for length 13)
        from (irb):4:in `[]'
        from (irb):4

結局、Javaの配列はRubyの配列とは別扱いってことなん
だろうねぇ。こないだもto_sym使ってRubyの配列を
Javaの配列に変換してJava APIに渡すこと書いたけど。
今度は逆のパターンだね。Java APIから返ってくるのは
Javaの配列で、それはRubyの配列とは違うと。

あ、でも、Javaの配列はto_aすりゃいいのか:

irb(main):004:0> months = months.to_a
=> ["1\346\234\210", "2\346\234\210", "3\346\234\210", "4\346\234\210", "5\346\2
34\210", "6\346\234\210", "7\346\234\210", "8\346\234\210", "9\346\234\210", "10
\346\234\210", "11\346\234\210", "12\346\234\210", ""]
irb(main):005:0> months[-1]
=> ""

ちなみにUTF-8の出力で文字化けしてます。

本家Permlink


2010年01月13日

jrubでJavaクラスのサブクラスを作るとき、
コンストラクタの扱いがよくわかんないんだよね。

Javaクラスのコンストラクタと違うコンストラクタを
作ることはできないみたいだし:

  class Rule < JComponent
    def initialize(o, m)
      @orientation = o
      @isMetric = m
      @increment = 0
      @units = 0
      setIncrementAndUnits
    end
    ...

initializeもコンストラクタとは微妙に違う扱いに
なってて:

  class DrawingPane < JPanel
    include MouseListener
  
    def initialize
      @circles = []
      @area = Dimension.new(0, 0)
      addMouseListener(self)
    end
    ...

みたいのだとaddMouseListenerでエラーになる。

だから、結局、newしてさらに初期化するみたいな
書き方になっちゃう:

  pane = DrawingPane.new
  pane.init

本家Permlink


2010年01月11日

JPasswordFieldのTutorialなんだけど:

http://java.sun.com/docs/books/tutorial/uiswing/components/passwordfield.html

ここにこう書いてある:

  Security note: Although the JPasswordField class inherits the
  getText method, you should use the getPassword method instead. Not
  only is getText less secure, but in the future it might return the
  visible string (for example, "******") instead of the typed string.

  To further enhance security, once you are finished with the
  character array returned by the getPassword method, you should set
  each of its elements to zero. The preceding code snippet shows how
  to do this.

最初の段落の話はわかるんだけど、次の段落が何を
いってるかわかんない。

ソースコードで確かめたんだけど、JPasswordField#getPasswordが
返すchar[]は、新しくnewされた配列で、JPasswordFieldが
実際に抱えているものじゃない。実際に入力された
文字列を抱えてるのはDocumentオブジェクトだし。
だったら、getPasswordで返された配列をクリアしたって、
あんま意味ない気がするんだけど。

まぁ、あれかな。GCに消されるまでに時間があるかも
しれないから、いらなくなったらとっととクリア
しときなさいっていう程度の話かな。

いや、でも、それだったら、JPasswordField#clearか
なんかを用意したほうがいいと思うけど。

--

上の中で``zero''ってあるけど、それは'0'のことね。
少なくともTutorialのサンプルではそうなってる。

本家Permlink


2010年01月10日

Swing Tutorialでこんなコードがあった:

  NumberFormat paymentFormat = NumberFormat.getCurrencyInstance();
  JFormattedTextField paymentField = new JFormattedTextField(paymentFormat);

これを素直にjrubyで書き直した:

  paymentFormat = NumberFormat.getCurrencyInstance
  paymentField = JFormattedTextField.new(paymentFormat)

ところが、これがうまく動かない。いろいろ調べてみると、
JFormattedTextFieldのコンストラクタが原因のようだ。

JFormattedTextFieldのコンストラクタはいくつかあるが、
1つの引数を取るものが複数ある:

  JFormattedTextField(Format format)
  JFormattedTextField(JFormattedTextField.AbstractFormatter formatter)
  JFormattedTextField(JFormattedTextField.AbstractFormatterFactory factory)
  JFormattedTextField(Object value)

このうち、上のjrubyのコードで呼び出したコンストラクタは、
もちろん、最初のFormatを受け取るコンストラクタの
つもりだった。しかし、それがどういうわけかObjectを
受け取るコンストラクタを呼び出しているようだ。

そこで、TextFieldのコンストラクトとFormatのセットを
分けてやることにした:

  paymentField = JFormattedTextField.new
  paymentField.???

しかし、JFormattedTextFieldのAPIドキュメントを見ると、
setFormatなるメソッドは存在しない。あるのはsetFormatter
とsetFormatterFactoryだけ。そこでまず、setFormatterを
使ってみた:

  paymentField = JFormattedTextField.new
  paymentFormat = NumberFormat.getCurrencyInstance
  paymentField.setFormatter(NumberFormatter.new(paymentFormat))

ところが、これで実際に動かしてみると、Javaのコードと
動きが異なることがわかった。そこで、JFormattedTextField
のソース・コードを見ることにした:

    public JFormattedTextField(java.text.Format format) {
        this();
        setFormatterFactory(getDefaultFormatterFactory(format));
    }

というわけで、setFormatterFactoryを使うように修正した:

  paymentField = JFormattedTextField.new
  paymentFormat = NumberFormat.getCurrencyInstance
  factory = DefaultFormatterFactory.new(NumberFormatter.new(paymentFormat))
  paymentField.setFormatterFactory(factory)

と、まぁ、こういったわけで、jrubyからSwingを使えると
いっても、少ないながらも落とし穴はあって、それを
避けるにはJavaの知識が多少なりとも必要になる。

ただ、これはどのGUIライブラリを使うにしてもいえる
ことで、Ruby/TkならTclの知識が必要になるし、Gtkなら
C言語の知識が必要になる。そこらへんもGUIライブラリを
選ぶときのポイントではある。

本家Permlink


2010年01月06日

Swing TutorialのFileChooserDemo.javaをjrubyで書き
直してたんだけど、こういうメソッドがあった:

  def createContentPane
    contentPane = JPanel.new(BorderLayout.new)
    @log = javax.swing.JTextArea.new(5, 20)
    @log.setMargin(java.awt.Insets.new(5, 5, 5, 5))
    @log.setEditable(false)
    logScrollPane = javax.swing.JScrollPane.new(@log)

    @fc = JFileChooser.new

    @openButton = JButton.new("Open a File...",
                              createImageIcon("images/Open16.gif"))
    @openButton.addActionListener(self)

    @saveButton = JButton.new("Save a File...",
                              createImageIcon("images/Save16.gif"))
    @saveButton.addActionListener(self)

    buttonPanel = JPanel.new
    buttonPanel.add(@openButton)
    buttonPanel.add(@saveButton)

    contentPane.add(buttonPanel, BorderLayout::PAGE_START)
    contentPane.add(logScrollPane, BorderLayout::CENTER)
    return contentPane
  end

まぁ、このままでもよかったんだけど、長いと感じたんで
extract methodすることにした。その結果:

  def createContentPane
    @fc = JFileChooser.new
    contentPane = JPanel.new(BorderLayout.new)
    addButtonPane(contentPane)
    addLogPane(contentPane)
    return contentPane
  end

  def addButtonPane(contentPane)
    buttonPanel = JPanel.new

    @openButton = JButton.new("Open a File...",
                              createImageIcon("images/Open16.gif"))
    @openButton.addActionListener(self)
    buttonPanel.add(@openButton)

    @saveButton = JButton.new("Save a File...",
                              createImageIcon("images/Save16.gif"))
    @saveButton.addActionListener(self)
    buttonPanel.add(@saveButton)

    contentPane.add(buttonPanel, BorderLayout::PAGE_START)
  end

  def addLogPane(contentPane)
    @log = javax.swing.JTextArea.new(5, 20)
    @log.setMargin(java.awt.Insets.new(5, 5, 5, 5))
    @log.setEditable(false)
    logScrollPane = javax.swing.JScrollPane.new(@log)
    contentPane.add(logScrollPane, BorderLayout::CENTER)
  end

こういうふうに、大きな部品を先に作って、次にそこに
ハマる部品を作って、すぐにハメていくっていう。まぁ、
徹底できてるわけじゃないけどね。

一方で、小さい部品をぜんぶ作って、それがハマる大きな
部品作って、それからハメるっていうのもあるんだけど。

前者がトップダウンなら、後者ボトムアップといえるん
だけど。こないだから書いてるように、自分はトップ
ダウンのほうが好きなのね。

部品が増えてくると、たくさんある小さい部品をかき
集めて組み立てるっていうのは煩雑になるんだよね。
トップダウンだとコードを見て『あ、ボタン載っける
パネルは終わったな、じゃあ、次はテキストエリア
載っけるパネルだな』っていうのがわかりやすい。

新しい部品を追加する場合でも、ボトムアップだと、
生成するところとハメるところと分散しがちなんだけど、
トップダウンだと『ここ』っていうのが特定しやすい。

まぁ、トップダウンっていうより深さ優先っていった
ほうがいいかもしれない。

本家Permlink


2010年01月04日

Java Tutorialは、自分の気に入らない書き方してるのが
多くって、たとえば:

        ButtonGroup group = new ButtonGroup();
        final int numButtons = 4;
        JRadioButton[] radioButtons = new JRadioButton[numButtons];

        radioButtons[0] = new JRadioButton("OK (in the L&F's words)");
        radioButtons[0].setActionCommand(defaultMessageCommand);

        radioButtons[1] = new JRadioButton("Yes/No (in the L&F's words)");
        radioButtons[1].setActionCommand(yesNoCommand);

        radioButtons[2] = new JRadioButton("Yes/No "
                                           + "(in the programmer's words)");
        radioButtons[2].setActionCommand(yeahNahCommand);

        radioButtons[3] = new JRadioButton("Yes/No/Cancel "
                                           + "(in the programmer's words)");
        radioButtons[3].setActionCommand(yncCommand);

        for (int i = 0; i < numButtons; i++) {
            group.add(radioButtons[i]);
        }
        radioButtons[0].setSelected(true);

みたいなコード。こういうボタンを配列に突っ込むのは
マズい場合が多くって。まぁ、今回はラジオ・ボタン
だから、それほどマズくないっちゃないんだけど。
でも、最後に:

        for (int i = 0; i < numButtons; i++) {
            group.add(radioButtons[i]);
        }

ってやってるのはダメなのね。

前にも書いたけど、こんなものはループで回さずにベタに
書いたほうがいい:

        radioButtons[0] = new JRadioButton("OK (in the L&F's words)");
        radioButtons[0].setActionCommand(defaultMessageCommand);
        group.add(radioButtons[0]);

        radioButtons[1] = new JRadioButton("Yes/No (in the L&F's words)");
        radioButtons[1].setActionCommand(yesNoCommand);
        group.add(radioButtons[1]);

        radioButtons[2] = new JRadioButton("Yes/No "
                                           + "(in the programmer's words)");
        radioButtons[2].setActionCommand(yeahNahCommand);
        group.add(radioButtons[2]);

        radioButtons[3] = new JRadioButton("Yes/No/Cancel "
                                           + "(in the programmer's words)");
        radioButtons[3].setActionCommand(yncCommand);
        group.add(radioButtons[3]);

        radioButtons[0].setSelected(true);

なぜ、こっちのほうがいいかっていうと、関数を切り出し
やすくなるから:

    JRadioButton createRadioButton(String title, String command, ButtonGroup group) {
        JRadioButton result = new JRadioButton(title);
        result.setActionCommand(command);
        group.add(result);
    }

で、これを使って書き直すと:

        radioButtons[0] = createRadioButton("OK (in the L&F's words)",
                                            defaultMessageCommand, group);
        radioButtons[1] = createRadioButton("Yes/No (in the L&F's words)",
                                            yesNoCommand, group);
        radioButtons[2] = createRadioButton("Yes/No "
                                            + "(in the programmer's words)",
                                            yeahNahCommand, group);
        radioButtons[3] = createRadioButton("Yes/No/Cancel "
                                            + "(in the programmer's words)",
                                            yncCommand, group);

っていう具合になる。

ところで、関数の切り出し方は他にも考えられる。
たとえば:

    JRadioButton[] creareRadioButtons(String[] titles) {
        JRadioButton[] results = new JRadioButton[titles.length];
        for (int i = 0; i < titles; ++i) {
            results[i] = new JRadioButton(titles[i]);
        }
        return results;
    }

    void setActionCommands(JRadioButton[] buttons, String[] commands) {
        for (int i = 0; i < buttons.length; ++i) {
            buttons[i].setActionCommand(commands);
        }
    }

けれども、これは悪い書き方だと自分は思ってるのね。
1つのボタンを作るんなら1つのボタンに集中して一気に
作り上げたほうがいい。

GUI作るときは基本的にシーケンシャルなんだよね。
パーツを1つずつ完成させてハメ込んでいくっていう。
そうでないと、パーツを動かしにくくなっちゃう。動かす
っていうのは、それをハメ込む場所を移すっていう意味で。

本家Permlink


2010年01月03日

jrubyはむつかしい。

  panels = [CrayonPanel.new]
  symbol = "javax.swing.colorchooser.AbstractColorChooserPanel".to_sym
  @tcc.setChooserPanels(panels.to_java(symbol))

とか。これは:

http://java.sun.com/docs/books/tutorial/uiswing/components/colorchooser.html

をjrubyで書き直したときのもんだけど。これやってた
とき、他にもClassLoaderがnilになるっていう現象が
出て。前までは:

  imgURL = self.java_class.class_loader.getResource(path)

で動いてたんだけど。どうも親クラスがJavaのクラスの
サブクラスだとダメなのかな? 結局:

  imgURL = java.lang.ClassLoader.getSystemClassLoader.getResource(path)

で動いた。これが一番いいのかな。まぁ、ClassLoader
なんて、jruby使ってるときは替わるもんじゃないだろうし。

Swingのチュートリアル、書き溜まったら公開してみっかなぁ。

本家Permlink


Copyright © 1905 tko at jitu.org

バカが征く on Rails