Smalltalkのtは小文字です

id:sumim:about

オブジェクト指向の“モヤッと”の正体を知りたくなったら、id:sumim:20040525:p1 がお役に立つかも…。


 

2010-07-27

[] 話題の Big Scripting Languages チートシートSmalltalk の空欄を埋めてみた [後半]


Smalltalk が普通に入っているのがすばらしいので、すこしだけ目立ち気味の空欄を GNU Smalltalk、SqueakVisualWorks で項目を分けてからざっと調べて分かる範囲で埋めてみた―の巻の後半部分です。GSTer、VWer は、訂正・補足をお願いします。もちろん Squeak使いからのツッコミも歓迎です。



libraries and modules
> ruby GNU Smalltalk Squeak VisualWorks
import library require PackageLoader fileInPackage: 'PackageName'
PackageLoader fileInPackages: #('Package1' 'Package2')
use SqueakMap Package Loader or drag’n drop package file to desktop or
(Installer file: 'path/to/package/file') install
use Parcel Manager and Load Parcel Named... command or ParcelManag
library path $: ?? SMSqueakMap default directory UISettings preferenceFor: #parcelPath
library path environment variable RUBYLIB SMALLTALK_MODULES none none
library path command line option -I none, use gst-load script none -filein
module declaration class Foo or module Foo write package.xml and use gst-package script use Monticello Store.Registry packageNamedOrCreate: 'Foo'
module separator :: . (period) none . (period)
package management $ gem list
$ gem install rails
PackageLoader SMSqueakMap, Monticello ParcelManager, StORE, Monticello
objects
> ruby GNU Smalltlk Squeak VisualWorks
define class class Int
  attr_accessor :value
  def initialize(i=0)
    @value = i
  end
end
Object subclass: Int [
  | val |
  initialize [
    val := 0
  ]
  val [^val]
  val: i [val := i]
  Int class >> new [
    ^super new initialize; yourself
  ]
  Int class >> new: i [
    ^self new val: i; yourself
  ]
]
in class browser
Object subclass: #Int
  instanceVariableNames: 'val'
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Category-Name'

use browser’s "create inst var accessors" command

Int >> initialize
  super initialize
  val := 0

Int clas >> new: i
  ^self new val: i; yourself
use class browser’s New Class...   dialog and set Create Methods: options for creating isnt var asscessors

in class browser

Int >> initialize
  super initialize.
  val := 0.
  ^self

Int clas >> new: i
  ^self new initialize val: i; yourself
create object i = Int.new
i = Int.new(4)
i := Int new
i := Int new: 4
i := Int new
i := Int new: 4
i := Int new
i := Int new: 4
invoke getter, setter v = i.value
i.value = v+1
v := i val.
i val: v + 1
v := i val.
i val: v + 1
v := i val.
i val: v + 1
instance variable accessibility private by default; use attr_reader, attr_writer, attr_accessor to make public private by default; create accessors to make public private by default; create accessors to make public private by default; create accessors to make public
define method in class body:
def plus(i)
  value + i
end
Int extend [
  plus: i [^val + i]
]
in class browser:

Int >> plus: i
  ^val + i
in class browser:

Int >> plus: i
  ^val + i
invoke method i.plus(7) i plus: 7 i plus: 7 i plus: 7
cascading none i val: 4; plus: 7 i val: 4; plus: 7 i val: 4; plus: 7
destructor val = i.value
ObjectSpace.define_finalizer(i) {
  puts "bye, #{val}"
}
i addToBeFinalized

define finalize method beforehand

Int extend [
  finalize [
  ('bye, ', val printString) displayNl.
  super finalize]
]
WeakRegistry default add: i

define finalize method beforehand

Int >> finalize
  Transcript cr; show: 'bye, ', val printString.
  super finalize

weakly := i withLastRites: [:obj | Transcript show: 'bye, ', obj val printString; cr]
method missing in class body:
def method_missing(name, *a)
  puts "no def: #{name}" +
    " arity: #{a.size}"
end
Int extend [
  doesNotUnderstand: msg [
  | sel args |
  sel := msg selector.
  args := msg arguments.
  ('no def: ', sel, ' args: ', args printString) displayNl]
]
Int >> doesNotUnderstand: msg
  Transcript cr; show: (
    'no def: {1} args: {2}' format:
      {msg selector. msg arguments})
Int >> doesNotUnderstand: msg
  | sel args |
  sel := msg selector.
  args := msg arguments.
  Transcript cr; show: ('no def: <1s> args: <2p>'
    expandMacrosWith: sel with: args)
inheritance class Counter < Int
  @@instances = 0
  def initialize
    @@instances += 1
    super
  end
  def incr
    self.value += 1
  end
  def self.instances
    @@instances
  end
end
Int subclass: Counter [
  incr [val := val + 1]
]

Counter class extend [
  Insts := 0.
  insts [^Insts]
]

Counter extend [
  initialize [
    super initialize.
    Insts := Insts + 1
  ]
]
use class browser
Int subclass: #Counter
  instanceVariableNames: ''
  classVariableNames: 'Insts'
  poolDictionaries: ''
  category: 'Category-Name'

Counter class >> initialize
  Insts := 0

Counter class >> insts
  ^Insts

Counter >> initialize
  super initialize.
  Insts := Insts + 1

Counter >> incr
  val := val + 1

don’t forget to invoke the class initializer
Counter initialize
use class browser’s New Class...  dialog and create a shared variable Insts in Shared Variable tab

in class browser

Count class >> initialize
  Insts := 0

Counter class >> insts
  ^Insts

Counter >> initialize
  super initialize.
  Insts := Insts + 1.
  ^self

Counter >> incr
  val := val + 1

don’t forget to invoke the class initializer
Counter initialize
invoke class method Counter.instances Counter insts Counter insts Counter insts
reflection and hooks
> ruby GNU Smalltalk Squeak VisualWorks
class a.class a class a class a class
has method? a.respond_to?('reverse') a respondsTo: #reverse a respondsTo: #reverse a respondsTo: #reverse
message passing (1..9).each { |i| a.send("phone#{i}=", nil) } (1 to: 9) do: [:i |
  a perform: ('phone', i printString, ':') asSymbol with: nil]
(1 to: 9) do: [:i |
  a perform: ('phone', i printString, ':') asSymbol with: nil]
(1 to: 9) do: [:i |
  a perform: ('phone', i printString, ':') asSymbol with: nil]
eval loop do
  puts eval(gets)
end
[Object
  evaluate: stdin nextLine, ' displayNl'
  ifError: [ repeat
[Transcript cr; show:
  (Compiler evaluate:
    (FillInTheBlank request: ''))
] repeat

[Transcript cr; show:
  (Compiler evaluate:
    (Dialog request: ''))
] repeat
methods a.methods a class selectors a class selectors a class selectors
attributes a.instance_variables a class instVarNames a class instVarNames a class instVarNames
pretty print require 'pp'
h = { 'foo'=>1, 'bar'=>[2,3] }
pp h
none none none
source line number and file name __LINE__
__FILE__
(Integer >> #factorial) methodSourcePos
(Integer >> #factorial) methodSourceFile

you can also get method source code
(Counter >> #incr) methodSourceCode
(Integer >> #factorial) fileIndex
(Integer >> #factorial) filePosition

you can also get method source code
(Counter >> #incr) getSource
(Integer compiledMethodAt: #factorial) fileIndex
(Integer compiledMethodAt: #factorial) filePosition

you can also get method source code
(Integer compiledMethodAt: #factorial) getSource
web
ruby GNU Smalltalk Squeak VisualWorks
http get require 'net/http'
h = 'www.google.com'
r = Net::HTTP.start(h, 80) do |f|
  f.get('/')
end
r.body
h := 'www.google.com:80'.
r := HTTPSocket httpGetDocument: h.
r contents
url encode/decode require 'cgi'
CGI::escape('hello world')
CGI::unescape('hello+world')
none
create repo and start server $ rails foo
$ cd foo
$ ./script/server
WAKom startOn: 8888.

WAComponent subclass: #Foo
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Seaside-App'

Foo registerAsApplication: 'foo'
web framework Rails Seaside Seaside Seaside
object relational mapper ActiveRecord DBI GLORP GLORP
templates ERB ?? ?? ??
java interoperation
ruby GNU Smalltalk Squeak VisualWorks
version JRuby 1.4.0 none JNIPort, JavaConnect JNIPort, JavaConnect
repl jirb none, use a Workspace none, use a Workspace
interpreter jruby none, use a Workspace none, use a Workspace
compiler jrubyc none, use a Workspace none, use a Workspace
prologue none none none
new rnd = java.util.Random.new rnd := (JVM current findClass: 'java.util.Random') new rnd := (JVM current findClass: 'java.util.Random') new_null.
method rnd.next_float rnd nextFloat rnd nextFloat_null
import java_import java.util.Random
rnd = Random.new
none none
non-bundled java libraries require 'path/to/mycode.jar' none none
shadowing avoidance module JavaIO
  include_package "java.io"
end
none none
to java array [1,2,3].to_java(Java::int) JavaIntArray from: #(1 2 3) jvm: JVM current JavaIntArray from: #(1 2 3) jvm: JVM current
java class subclassable? yes no no
java class open? yes no no
_______________________ _______________________ _______________________ _______________________


partially copied from http://hyperpolyglot.wikidot.com/scripting then modified by sumim
and also licensed under Creative Commons Attribution-ShareAlike 3.0

2010-07-26

[] 話題の Big Scripting Languages チートシートSmalltalk の空欄を埋めてみた [前半]


Smalltalk が普通に入っているのがすばらしいですね。せっかくなので、すこしだけ目立ち気味の空欄を GNU Smalltalk、SqueakVisualWorks で項目を分けてからざっと調べて分かる範囲で埋めてみました。スペースの都合と比較のしやすさから、他の言語は Ruby だけ残してありますので、あしからず。GSTer、VWer は、訂正・補足をお願いします。もちろん Squeak使いからのツッコミも歓迎です。


> ruby (1995) GNU Smalltalk (1990) Squeak (1996; 1983, Apple Smalltalk) VisualWorks (1993; 1980, XEROX Smalltalk-80)
versions used 1.8.7; 1.9.1 3.2 4.1 7.7
show version $ ruby --version Smalltalk version

$ gst --version
Smalltalk version SystemUtils version
interpreter $ ruby foo.rb $ gst foo.st none $ visual your.im -filein script.st
repl $ irb $ gst use a Workspace use a Workspace
check syntax $ ruby -c foo.rb none none none
flags for stronger and strongest warnings $ ruby -w foo.pl
$ ruby -W2 foo.pl
none none none
statement separator ; or sometimes newline . (period) or sometimes newline . (period) . (period)
block delimiters {} or do end [ ] [ ] [ ]
assignment a=1 a := 1 a := 1 a := 1
parallel assignment a,b,c = 1,2,3 none none none
swap a,b = b,a | tmp |
tmp := a. a := b. b := tmp
| tmp |
tmp := a. a := b. b := tmp
| tmp |
tmp := a. a := b. b := tmp
compound assignment operators: arithmetic, string, logical, bit += -= *= /= none %= **=
+=
&&= ||= ^=
<<= >>= &= |= ^=
none none none
increment and decrement x not mutated:
x = 1
x.succ
x.pred
none none none
variable declaration assignment; $ prefix denotes global scope Smalltalk at: #GLOBAL put: nil.
assignment; will be local to method
Smalltalk at: #GLOBAL put: nil. for global variable
| local1 local2 | for local variable
Smalltalk at: #GLOBAL put: nil. for global variable
| local1 local2 | for local variable
constant declaration warns if identifier with uppercase first character is reassigned
PI = 3.14
none (Smalltalk associationAt: #GLOBAL) beReadOnlyBinding

MyNameSpace.MyClass defineSharedVariable: #SharedVar
  private: false
  constant: true
  category: 'SharedVar Category'
  initializer: '3 + 4'
end-of-line comment # comment none none none
multiline comment =begin
comment line
another line
=end
"comment line
another line"
"comment line
another line"
"comment line
another line"
null nil nil nil nil
null test v == nil
v.nil?
v = nil
v == nil
v isNil
v = nil
v == nil
v isNil
v = nil
v == nil
v isNil
undefined variable access raises NameError compile error compile error compile error
undefined test ! defined?(v) Smalltalk includesKey: #GLOBAL

MyClass allClassVarNames includes: #ClassVar
Smalltalk includesKey: #GLOBAL

MyClass allClassVarNames includes: #ClassVar

thisContext tempNames includes: #tempVar
Smalltalk includesKey: #GLOBAL

MyClass allClassVarNames includes: #ClassVar

thisContext tempNames includes: #tempVar
arithmetic and logic
> ruby GNU Smalltalk Squeak VisualWorks
true and false true false true false true false true false
falsehoods false nil false false false
logical operators and or not also: && || ! and: or: not always evaluate 2nd operand: & | and: or: not always evaluate 2nd operand: & | and: or: not always evaluate 2nd operand: & |
conditional expression x > 0 ? x : -x x > 0 ifTrue: [x] ifFalse: [0 - x] x > 0 ifTrue: [x] ifFalse: [0 - x]
x positive ifTrue: [x] ifFalse: [x negated]
x > 0 ifTrue: [x] ifFalse: [0 - x]
x positive ifTrue: [x] ifFalse: [x negated]
comparison operators == != > < >= <= = ~= > < >= <= = ~= > < >= <= = ~= > < >= <=
convert from string, to string 7 + "12".to_i
73.9 + ".037".to_f
"value: " + "8".to_s
7 + ‘12’ asNumber
73.9 + '.037' asNumber
'value: ', 8 printString
7 + ‘12’ asNumber
73.9 + '.037' asNumber
'value: ', 8 printString
7 + ‘12’ asNumber
73.9 + '.037' asNumber
'value: ', 8 printString
arithmetic operators + - * x.fdiv(y) / % ** + - * / // \\ raisedTo: + - * / // \\ raisedTo: + - * / // \\ raisedTo: also: **
integer division a / b a // b a // b a // b
float division a.to_f / b or
a.fdiv(b)
a asFloat / b
closure of integers under / is the rationals
a asFloat / b
closure of integers under / is the rationals
a asFloat / b
closure of integers under / is the rationals
arithmetic functions include Math
sqrt exp log sin cos tan asin acos atan atan2
sqrt exp ln sin cos tan arcSin arcCos arcTan arcTan: sqrt exp ln sin cos tan arcSin arcCos arcTan arcTan: sqrt exp ln sin cos tan arcSin arcCos arcTan arcTan:
arithmetic truncation x.abs, x.round, x.ceil, x.floor x abs. x rounded. x ceiling. x floor x abs. x rounded. x ceiling. x floor x abs. x rounded. x ceiling. x floor
min and max [1,2,3].min
[1,2,3].max
none #(1 2 3) min
#(1 2 3) max
#(1 2 3) min
#(1 2 3) max
division by zero integer division raises ZeroDivisionError
float division returns Infinity
raises ZeroDivide raises ZeroDivide raises ZeroDivide
integer overflow becomes arbitrary length integer of type Bignum becomes arbitrary length integer of type LargePositiveInteger becomes arbitrary length integer of type LargePositiveInteger becomes arbitrary length integer of type LargePositiveInteger
float overflow Infinity Inf Infinity raises OverflowSignal
sqrt -2 raises Errno::EDOM returns NaN raises FloatingPointException raises ImaginaryResultSignal
rational numbers require 'rational'
x = Rational(22,7)
x.numerator
x.denominator
x := 22 / 7
x numerator
x denominator
x := 22 / 7
x numerator
x denominator
x := 22 / 7
x numerator
x denominator
complex numbers require 'complex'
z = 1 + 1.414.im
z.real
z.imag
PackageLoader fileInPackage: ‘Complex’.
z := 1 + 1.414 i
z real
z imaginary
z := 1 + 1.414 i
z real
z imaginary
load AT MetaNumerics parcel
z := 1 + 1.414 i
z real
z imaginary
random integer, uniform float, normal float rand(100)
rand
none
rand := Random new
rand between: 1 with: 100
rand next
next
rand := Random new
100 atRandom
rand next
next
rand := Random new
(rand next * 100) asInteger
rand next
none
bit operators << >> & | ^ ~ bitShift: bitAnd: bitOr: bitXor: bitInvert << >> bitAnd: bitOr: bitInvert also: bitShift: bitShift: bitAnd: bitOr: bitXor: bitInvert
strings
> ruby GNU Smalltalk Squeak VisualWroks
character literal none $A $A $A
chr and ord 65.chr
"A".ord
65 asCharacter
$A asciiValue
65 asCharacter
$A asciiValue
65 asCharacter
$A asInteger
string literal 'don\'t say "no"'
"don't say \"no\""
'don''t say "no"' 'don''t say "no"' 'don''t say "no"'
newline in literal yes yes (allows multiline) yes (allows multiline) yes (allows multiline)
here document computer = 'PC'
s = <<EOF
here document
there #{computer}
EOF
none none none
escapes single quoted:
\' \\
double quoted:
\a \b \cx \e \f \n \r \s \t \uhhhh \u{hhhhh} \v \xhh \ooo
none none none
encoding load the I18N package
UTF-8
UTF-8 UTF-8
variable interpolation count = 3
item = "ball"
puts "#{count} #{item}s"
none none, use format: or expandMacros
count := 3
item := 'ball'
'{1} {2}s' format: {count. item}
'<1p> <2s>s' expandMacrosWithArguments: {count. item}
none, use expandMacros
count := 3
item := 'ball'
'<1p> <2s>s' expandMacrosWith: count with: item
length "hello".length
"hello".size
'hello' size 'hello' size 'hello' size
character count '(3*(7+12))'.count('(') '(3*(7+12))' occurrencesOf: $( '(3*(7+12))' occurrencesOf: $( '(3*(7+12))' occurrencesOf: $(
index of substring "foo bar".index("bar") 'foo bar' indexOfSubCollection: 'bar' 'foo bar' findString: 'bar' 'foo bar' findString: 'bar' startingAt: 1
extract substring "foo bar"[4,3] 'foo bar' copyFrom: 5 to: 7 'foo bar' copyFrom: 5 to: 7 'foo bar' copyFrom: 5 to: 7
concatenate "hello, " + "world" 'hello, ', 'world' 'hello, ', 'world' 'hello, ', 'world'
split "foo bar baz".split 'foo bar baz' subStrings 'foo bar baz' subStrings 'foo bar baz' tokensBasedOn: ' '
join ['foo','bar','baz'].join(' ') #('foo' 'bar' 'baz') join: ' ' none, use asStringOn:delimiter:
String streamContents: [:ss | #('foo' 'bar' 'baz') asStringOn: ss delimiter: ' ']
none
scan "hello, hep cat".scan(/\w+/) 'hello, hep cat' allOccurrencesOfRegex: '\w+' none none
pack and unpack f="a5ilfd"
s=["hello",7,7,3.14,3.14].pack(f)
s.unpack(f)
none none none
sprintf "tie: %s %d %f" % ['Spain',13,3.7] none none none
case manipulation "hello".upcase
"HELLO".downcase
"hello".capitalize
'hello' asUppercase
'hello' asLowercase
none
'hello' asUppercase
'hello' asLowercase
'hello' capitalized
'hello' asUppercase
'hello' asLowercase
'hello' capitalized
strip " foo ".strip
" foo".lstrip
"foo ".rstrip
none ' foo ' withBlanksTrimmed
' foo' withoutLeadingBlanks
'foo ' withoutTrailingBlanks
none
pad on right, on left "hello".ljust(10)
"hello".rjust(10)
none 'hello' forceTo: 10 paddingWith: $
'hello' forceTo: 10 paddingStartWith: $
none
character translation "hello".tr('a-z','n-za-m') none none, use translateWith: none
regexp match "1999".match(/^\d{4}$/)
"foo BAR".match(/^[a-z]+/)
"foo BAR".match(/[A-Z]+/)
'1999' =~ '^\d{4}$'
'foo BAR' =~ '^[a-z]+'
'foo BAR' =~ '[A-Z]+'
none none
match, prematch, postmatch s = "A 17 B 12"
while (/\d+/.match(s)) do
 &nbspdiscard = $‘
 &nbspnumber = $&
 &nbsps = $’
 &nbspputs number
end
none, use onOccurrencesOfRegex:do:

s := 'A 17 B 12'.
s onOccurrencesOfRegex: '\d+'
 do: [:each | each match displayNl]
none none
substring matches reg = /(\d{4})-(\d{2})-(\d{2})/
m = reg.match("2010-06-03")
yr,mn,dy = m[1..3]
reg := '(\d{4})-(\d{2})-(\d{2})'.
m := '2010-06-03' =~ reg.
yr := m at: 1.
mn := m at: 2.
dy := m at: 3
none none
single substitution "teh last of teh Mohicans".sub(/teh/,'the') 'teh last of teh Mohicans' replacingRegex: 'teh' with: 'the' none none
global substitution "teh last of teh Mohicans".gsub(/teh/,'the') 'teh last of teh Mohicans' replacingAllRegex: 'teh' with: 'the' none none
containers
> ruby GNU Smalltalk Squeak VisualWorks
array literal nums = [1,2,3,4] nums := #(1 2 3 4) nums := #(1 2 3 4) nums := #(1 2 3 4)
array size nums.size or
nums.length
nums size nums size nums size
array lookup nums[0] nums at: 1 nums at: 1 nums at: 1
array slice nums[1..2] nums copyFrom: 2 to: 3 nums copyFrom: 2 to: 3 nums copyFrom: 2 to: 3
array iteration [1,2,3].each { |i| puts i } nums do: [:o | o displayNl] nums do: [:o | Transcript cr; show: o] nums do: [:o | Transcript show: o; cr]
membership nums.include?(7) nums includes: 7 nums includes: 7 nums includes: 7
intersection [1,2] & [2,3,4] none, use select: #(1 2) intersection: #(2 3 4) none, use select:
union [1,2] | [2,3,4] none, use asSet #(1 2) union: #(2 3 4) none, use asSet
map [1,2,3].map { |o| o*o } #(1 2 3) collect: [:o | o*o] #(1 2 3) collect: [:o | o*o] #(1 2 3) collect: [:o | o*o]
filter [1,2,3].select { |o| o > 1 } #(1 2 3) select: [:o | o > 1] #(1 2 3) select: [:o | o > 1] #(1 2 3) select: [:o | o > 1]
reduce [1,2,3].inject(0) { |m,o| m+o } #(1 2 3) inject: 0 into: [:m :o| m + o]
#(1 2 3) fold: [:m :o | m + o]
#(1 2 3) inject: 0 into: [:m :o| m + o] #(1 2 3) inject: 0 into: [:m :o| m + o]
#(1 2 3) fold: [:m :o | m + o]
universal predicate [1,2,3,4].all? {|i| i.even? } #(1 2 3 4) allSatisfy: [:o | o even] #(1 2 3 4) allSatisfy: [:o | o even] #(1 2 3 4) allSatisfy: [:o | o even]
existential predicate [1,2,3,4].any? {|i| i.even? } #(1 2 3 4) anySatisfy: [:o | o even] #(1 2 3 4) anySatisfy: [:o | o even] #(1 2 3 4) anySatisfy: [:o | o even]
map literal h = { 't' => 1, 'f' => 0 } h := Dictionary new add: 't'->1; add: 'f'->0; yourself h := {'t'->1. 'f'->0} as: Dictionary

h := Dictionary new add: 't'->1; add: 'f'->0; yourself
h := Dictionary new add: 't'->1; add: 'f'->0; yourself
map size h.size or
h.length
h size h size h size
map lookup h['t'] h at: 't' h at: 't' h at: 't'
is map key present h.has_key?('y') h includesKey: 'y' h includesKey: 'y' h includesKey: 'y'
map iteration h.each { |k,v| code} h keysAndValuesDo: [:k :v | code] h keysAndValuesDo: [:k :v | code] h keysAndValuesDo: [:k :v | code]
keys and values of map as array h.keys
h.values
h keys
h values
h keys
h values
h keys
h values
out of bounds behavior nil raises IndexOutOfRange raises error raises error
functions
> ruby GNU Smalltalk Squeak VisualWorks
function declaration def add(a,b); a+b; end add := [:a :b | a+b] add := [:a :b | a+b] add := [:a :b | a+b]
function invocation add(1,2) add value: 1 value: 2
add valueWithArguments: #(1 2)
add valueWithArguments: {1. 2}
add value: 1 value: 2
add valueWithArguments: #(1 2)
add valueWithArguments: {1. 2}
add value: 1 value: 2
add valueWithArguments: #(1 2)
missing argument raises ArgumentError raises WrongArgumentCount raises error raises error
default value def log(x,base=10) none none none
arbitrary number of arguments def add(first, *rest)
 &nbspif rest.empty?
    first
 &nbspelse
    first + add(*rest)
 &nbspend
end
none none none
named parameter definition def f(h) none none none
named parameter invocation f(:eps => 0.01) none none none
pass by reference none none none
return value return arg or last expression evaluated ^ arg or last expression evaluated ^ arg or last expression evaluated ^ arg or last expression evaluated
multiple return values none none none
lambda declaration f = lambda { |x| x * x } f := [:x | x * x] f := [:x | x * x] f := [:x | x * x]
lambda invocation f.call(2) f value: 2 f value: 2 f value: 2
default scope local local local local
nested function definition not allowed not allowed (nested method definition) not allowed (nested method definition) not allowed (nested method definition)
execution control
> ruby GNU Smalltalk Squeak VisualWorks
if if n == 0
 &nbspputs "no hits"
elsif 1 == n
 &nbspputs "1 hit"
else
 &nbspputs "#{n} hits"
end
(n = 0
  ifTrue: ['no hits']
  ifFalse: [n = 1
    ifTrue: ['1 hit']
    ifFalse: [n printString, ' hits') displayNl
Transcript cr; show: (n = 0
  ifTrue: ['no hits']
  ifFalse: [n = 1
    ifTrue: ['1 hit']
    ifFalse: [n printString, ' hits')

or use case-switch like caseOf:otherwise: method

Transcript cr; show: (
  n caseOf: {
    [0] -> ['no hits'].
    [1] -> ['1 hit']}
  otherwise:[n printString, ' hits'])
Transcript show: (n = 0
 ifTrue: ['no hits']
  ifFalse: [n = 1
    ifTrue: ['1 hit']
    ifFalse: [n printString, ' hits'); cr
while while i < 100
  i += 1
[i < 100] whileTrue: [i := i + 1]
[i >= 100] whileFalse: [i := i + 1]
[i := i + 1. i< 100] whileTrue
[i := i + 1. i >= 100] whileFalse
[i < 100] whileTrue: [i := i + 1]
[i >= 100] whileFalse: [i := i + 1]
[i := i + 1. i< 100] whileTrue
[i := i + 1. i >= 100] whileFalse
i < 100] whileTrue: [i := i + 1]
[i >= 100] whileFalse: [i := i + 1]
[i := i + 1. i< 100] whileTrue
[i := i + 1. i >= 100] whileFalse
break/continue/redo break, next, redo none none none
[:break |
  [i := i + 1. i > 100 ifTrue: [break value repeat
] valueWithExit
for none none; use to:by:do: method

10 to: 1 by: -1 do: [:i |
  i displayNl
]
none; use to:by:do: method

10 to: 1 by: -1 do: [:i |
  Transcript cr; show: i
]
none; use to:by:do: method

10 to: 1 by: -1 do: [:i |
  Transcript show: i; cr
]
range iteration (1..10).each { |i| puts i } (1 to: 10) do: [:i | i displayNl] (1 to: 10) do: [:i | Transcript cr; show: i] (1 to: 10) do: [:i | Transcript show: i; cr]
statement modifiers
raise exception raise "bad arg" self error: 'bad arg' self error: 'bad arg' self error: 'bad arg'
catch exception begin
  risky
rescue
  puts "risky failed"
end
[risky]
  ifError: ['risky failed' displayNl]

[risky]
  on: Exception
  do: ['risky failed' displayNl]
[risky]
  ifError: [Transcript cr; show: 'risky failed']

[risky]
  on: Exception
  do: [Transcript cr; show: 'risky failed']
[risky]
  on: Exception
  do: [Transcript show: 'risky failed'; cr]
finally/ensure begin
  acquire_resource
  risky
ensure
  release_resource
end
[acquireResource. risky]
  ensure: [eleaseResource]
[acquireResource. risky]
  ensure: [eleaseResource]
[acquireResource. risky]
  ensure: [eleaseResource]
uncaught exception behavior stderr and exit stderr and exit notifier notifier
start thread t = Thread.new { sleep 10 } t := [(Delay forSeconds: 10) wait] fork t := [10 seconds asDelay wait] fork

t := [(Delay forSeconds: 10) wait] fork
t := [10 seconds wait] fork

t := [(Delay forSeconds: 10) wait] fork
wait on thread t.join none, use Semaphore, etc none, use Semaphore, etc none, use Semaphore, etc
environment and i/o
> ruby GNU Smalltalk Squeak VisualWorks
external command system('ls') stream := FileDescriptor popen: 'ls' dir: #read.
contents := stream contents.
stream close.
^contents
none, use OSProcess package ExternalProcess new cshOne: 'ls'
backticks `ls` ?? ?? ??
command line args ARGV.size, ARGV[0], ARGV[1],… Smalltalk arguments size, Smalltalk arguments at: 1 SmalltalkImage current arguments size, SmalltalkImage current argumentAt: 1 ??
print to standard out puts "hi world" 'hi world' displayNl none, use Transcript load Standard IO Streams parcel
OS.Stdout nextPutAll: 'hi world'; cr
standard file handles $stdin $stdout $stderr stdin stdout stderr none OS.Stdin OS.Stdout OS.Stderr
open file f = File.open('/etc/hosts') or
File.open('/etc/hosts') { |f|
f := FileStream open: '/etc/hosts’. or (File path: '/etc/hosts') withReadStreamDo: [:f | f := FileStream fileNamed: ‘/etc/hosts’ or FileStream fileNamed: ‘/etc/hosts’ do: [:f | f := '/etc/hosts' asFilename readStream
open file for writing f = File.open('/tmp/test','w') or
File.open('/tmp/test','w') { |f|
f := FileStream open: 'test.txt' mode: #write or (File path: '/etc/hosts') withWriteStreamDo: [:f | f := FileStream fileNamed: ‘/etc/hosts’ or FileStream fileNamed: ‘/etc/hosts’ do: [:f | f := '/etc/hosts' asFilename writeStream
close file f.close f close f close f close
read line f.gets f nextLine f nextLine f upTo: Character cr
iterate over a file by line f.each do |line| f do: [:char | f do: [:char | f do: [:char |
chomp line.chomp! none none none
read entire file into array or string a = f.lines.to_a
s = f.read
s := f contents s := f contents s := f contents
write to file f.write('hello') f nextPutAll: ‘hello’ f nextPutAll: ‘hello’ f nextPutAll: ‘hello’
flush file f.flush f flush f flush f flush
environment variable ENV['HOME'] Smalltalk getenv: 'HOME' none, use OSProcess package
UnixProcess env at: #HOME
SystemUtils getEnvironmentVariable: 'HOME’
exit exit(0) ObjectMemory quit: 0 none, use
SmalltalkIamge current snapshot: false andQuit: true
ObjectMemory quitWithError: 0
set signal handller Signal.trap("INT", lambda { |signo| puts "exiting…"; exit }) ?? none ??
_______________________ _______________________ _______________________ _______________________


partially copied from http://hyperpolyglot.wikidot.com/scripting then modified by sumim
and also licensed under Creative Commons Attribution-ShareAlike 3.0

2010-07-05

[] Objective-C の enumerateObjectsUsingBlock: の例を Squeak Smalltalk


Smalltalkってかなり昔からあるというのは知っています。

といいつつもSmalltalkのことわかってません....

※^これがでてくるところとか...

プログラマメモ2: enumerateObjectsUsingBlockの練習

何かの参考になれば…と、同じような処理を Squeak Smalltalk に書き直してみました。あと念のため、Smalltalk ではブロックとは関係なく return の意味で ^ を使います。Objective-C のブロックに出てくる ^ とは関係ありません。


| dataClass |

dataClass := Object subclass: #MyData
	instanceVariableNames: 'myname'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Category-Name'.

Browser new setClass: dataClass selector: nil; createInstVarAccessors.
| array enumerator |

"データをつくる"
array := OrderedCollection new.
0 to: 4 do: [:i |
	| mydata |
	mydata := MyData new.
	mydata myname: i asString.
	array add: mydata].

World findATranscript: nil.

"その場で定義してその場で使うパターン"
"中身の確認"
array do: [:obj | Transcript cr; show: '==> ', obj myname].

"BLOCKで使ってソート!!"
array sort: [:obj1 :obj2 |
	Transcript cr; show: ('[{1}]と[{2}]を比較' format: {obj1 myname. obj2 myname}).
	obj2 myname < obj1 myname].

"宣言したものを後から使うパターン"
enumerator := [:obj | Transcript cr; show: 'enumerator ==> ', obj myname].
array do: enumerator
==> 0
==> 1
==> 2
==> 3
==> 4
[0]と[1]を比較
[1]と[2]を比較
[3]と[4]を比較
[2]と[4]を比較
[2]と[3]を比較
enumerator ==> 4
enumerator ==> 3
enumerator ==> 2
enumerator ==> 1
enumerator ==> 0

2010-06-21

[] Cog VM リリース記念: SqueakRubyPython を恒例のフィボナッチベンチで戦わせてみる


Cog VM は、VisualWorks用の超高速 Smalltalk VM を手がけた Eliot Miranda 氏による Squeak Smalltalk 向けの新しい高性能 VM 。

Teleplace社(旧 Qwaq社)の製品である同名の仮想空間共有ソフトのベースである Croquet用に開発されたものですが、同社の厚意によりオープンソースとして公開され、Squeak のユーザーも利用可能になりました。



Win 向けのバイナリも公開されていたので、さっそく恒例のフィボナッチベンチ(39番目のフィボナッチ数 63245986 の再帰的な算出にかかる時間を計測。実装はナイーブなものにして、メモ化や遅延評価は使用しない)で人気のスクリプト言語(Ruby、Python)と戦わせてみました。結果はこんなかんじ(Core2 Duo 2.4GHz, Win Vista を使用)。

追記: Python3.1.2、Gauche0.9 の結果を追加。

追記: リクエストにおこたえして C と v8 の結果も追加。


 処理系                      速度[秒]                      
 Ruby1.8  123
 Ruby1.9  20.8
 Python2.5  50.3
 Python3.1  66.2
 Gauche0.9  16.0
 Squeak4.1 normal VM  15.6 (メソッド版), 63.5 (ブロック版)
 Squeak4.1 Cog VM  4.79 (メソッド版), 5.36 (ブロック版)
 VisualWorks7.7  1.65 (メソッド版), 3.34 (ブロック版)
 C  1.76
 v8-2.1  1.88

商用で爆速の Smalltalk 処理系である VisualWorks には一歩及ばないものの、ノーマルVM で“よい勝負”どまりだった Ruby1.9 に対し、新しい Cog VM ではメソッド版(Integer>>#fib として定義。39 fib で呼び出し)のみならず、大きく水をあけられていたブロック版(Ruby風に言えば Proc版。fib value: 39 で呼び出し)でも圧勝しています。すばらしいですね。


使用したコードと出力などを以下に示します。


Ruby
$ cat fib.rb
def fib(n)
  return n if n < 2
  fib(n-2) + fib(n-1)
end

n = ARGV[0].to_i
start = Time.now
puts fib(n), (Time.now - start).to_s + " sec"



$ ruby -v fib.rb 39
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
63245986
123.01 sec


$ ruby1.9 -v fib.rb 39
ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-cygwin]
63245986
20.767 sec

Python(2、3 共通)
$ cat fib.py
from sys import argv
from time import time

def fib(n):
  if n < 2:
    return n
  else:
    return fib(n-2) + fib(n-1)

n = int(argv[1])
start = time()
print(fib(n))
print(str(time() - start) + " sec")


$ python -V
Python 2.5.2


$ python fib.py 39
63245986
50.3140001297 sec


$ python3 -V
Python 3.1.2


$ python3 fib.py 39
63245986
66.2220001221 sec

Smalltalk(Squeak、VisualWorks 共通)
| fib mTime mRes bTime bRes |
Integer compile: 'fib
  ^(self < 2) ifTrue: [self] ifFalse: [(self - 2) fib + (self - 1) fib]'.

fib := nil.
fib := [:n | n < 2 ifTrue: [n] ifFalse: [(fib value: n-2) + (fib value: n-1)] ].

mTime := Time millisecondsToRun: [mRes := 39 fib].
bTime := Time millisecondsToRun: [bRes := fib value: 39].
^Array with: mTime -> mRes with: bTime -> bRes
"Squeak VM 4.0.2 => {15573 -> 63245986. 63477 -> 63245986} "
"Cog VM          => { 4793 -> 63245986.  5364 -> 63245986} "
"VisualWorks 7.7 => { 1651 -> 63245986.  3335 -> 63245986} "

Scheme(Gauche)
$ cat fib.scm
(define (fib n) (if (< n 2) n (+ (fib (- n 2)) (fib (- n 1)))))
(display (time (fib (string->number (car *argv*)))))


$ gosh -V fib.scm 10
Gauche scheme shell, version 0.9 [utf-8,pthreads], i686-pc-cygwin


$ gosh fib.scm 39
;(time (fib (string->number (car *argv*))))
; real  16.020
; user  15.054
; sys    0.015
63245986

C
$ cat fib.c
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

long fib(int n){
   if (n < 2) {
      return n;
   } else {
      return fib(n-2) + fib(n-1);
   }
}

int main(int argc, char *argv[]){
   clock_t startTime, endTime;
   long res;
   startTime = clock();
   res = fib(atoi(argv[1]));
   endTime = clock();
   printf("%ld, %f sec\n", res, (float)(endTime - startTime)/CLOCKS_PER_SEC);
   return 0;
}



$ gcc -Wall -O2 -o fib fib.c


$ ./fib 39
63245986, 1.762000 sec

JavaScript (v8)
$ cat fib.js
function fib(n) {
  if (n < 2) {
    return n;
  } else {
    return fib(n-2) + fib(n-1);
  }
}

var start = new Date();
print(fib(39));
print(new Date() - start + " msec");


$ v8-2.1/shell fib.js
63245986
1890 msec

2010-06-06

[] DCI パラダイムの提唱者、レインスカウ氏の BabyIDE で遊ぶ


BabyIDE は、古くは MVC(Model-View-Controller)、近年では DCI(Data-Context-Interaction)というパラダイムの提唱者として知られるトリグヴ・レインスカウ(Trygve Reenskaug)氏自らが手がける Squeak Smalltalk ベースの DCI 向けプログラミング&モデリングのためのツールで、彼の考える BabyUML(UML や他の言語の構成概念を基に作られたプログラミング規律)のための実験の場でもあります。


BabyIDE is an interactive development environment that supports the DCI paradigm with specialized browsers for each perspective. These browsers are placed in overlays within a common window so that the programmer can switch quickly between them. DCI with BabyIDE marks a new departure for object oriented programming technology.

Trygve/BabyIDE

ちなみに BabyIDE とか BabyUML に付いている Baby は、1948年にマンチェスター大で作られた世界初のノイマン型(プログラム内蔵型)コンピューターの一つである SSEM という試作機のニックネームであった「The Baby」がその由来らしいです。このプロジェクトの成果が赤ん坊がそうであるように、自分の足で立てるまでには成熟したものではない一方で伸びしろも有することを示すとと共に、BabyUML が仮想的なプログラム内蔵型コンピューターのようなものをターゲットにしていることともリンクさせているようです。

The world's first digital stored program computer was the Manchester Small Scale Experimental Machine―“The Baby”. This Baby was small, it was designed for testing the Williams-Kilburn cathode ray tube high speed storage device. It was a truly minimal computer with an operations repertoire of just 7 instructions. It executed its first program on 21st June 1948. The machine was insignificant in itself, but it marked the beginning of a new era.

The BabyUML project has created what may be the world's first integrated development environment based on a truly object oriented programming paradigm (Simula, Smalltalk, C++, Java, and others are based on the class paradigm. Even self code descibes one object at the time; there are no facilities for describing networks of collaborating objects). The result of the BabyUML project is like a new born baby. Its functionality is extremely limited. It cannot stand on its own two feet, but there is room for almost unlimited growth. My dream is that many people will adopt the Baby ideas and create their own vigorous variants.

http://folk.uio.no/trygver/2008/commonsense.pdf

I have somewhat whimsically chosen the name BabyUML. The English Baby was the world’s first stored program computer while the target for BabyUML is a virtual, stored program object computer that spans one or more hardware computers.

The BabyUML discipline of programming

後者については、ジム・コプリエン氏の「間」の概念への興味からも、アラン・ケイの“メッセージングのオブジェクト指向”において、オブジェクトが高速のネットワークで互いに接続された小さなコンピューターである、と例えられることとも無関係ではないように感じます。

Smalltalk object is a recursion on the entire possibilities of the computer. Thus its semantics are a bit like having thousands and thousands of computer all hooked together by a very fast network.

The Early History of Smalltalk

The big idea is "messaging" - that is what the kernal of Smalltalk/Squeak is all about (and it's something that was never quite completed in our Xerox PARC phase). The Japanese have a small word - ma - for "that which is in between" - perhaps the nearest English equivalent is "interstitial".

Alan Kay On Messaging


さて前置きはこのくらいにして、この BabyIDE を使い、id:digitalsoul さんによる Groovy および Scala によるローンシンジゲートの例を Squeak Smalltalk で書いてみます。なお、以下のコードで記述された仕様は Ruby札幌勉強会でのデモ向けのため、元エントリーのものに比べてかなり簡素化されているので、どうぞあしからず。



▼ BabyIDE のインストールと起動

Win 向けに動作に必要なファイル一式のアーカイブが公開されています。他の OS でも、その OS 向けの Squeak VM を別途用意すれば動作するはずです。

  • Trygve/BabyIDE から BabyIDE-1.ZIP をダウンロード
  • 適当な場所に展開
  • .image ファイルを Squeak.exe(Sqeuak VM)にドロップイン


▼ BB1クラスブラウザを開く

BabyIDE というだけあって、専用のクラスブラウザが用意されています。この Squeak イメージでは、ウインドウを開く操作の際には、矩形領域をしていないといけないようになっているので注意が必要です。使いづらければ、 SystemWindow>>#openInWorld: を versions からひとつ前のバージョンに revert すれば、通常通りの動きになります。

  • デスクトップメニュー → open... → BabyIDE... → BBLoanSyndicate と入力して Accept (alt + s)
  • 開きたいウインドウの位置と大きさを、左上から右下に向かってドラッグすることで指定(前述の revert 後は不要)

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate01.png



▼ データの定義

Squeak Smalltalk でクラスの定義をしたことがあれば、説明は不要かと思います。

  • コード枠(下段)にあるクラス定義テンプレートの NameOfSubclass のところを BBFacility に書き換え
  • instanceVariableNames: の引数である '' を 'limit loan shares' に書き換え
  • accept (alt + s) でコンパイル → BBFacility が左上のクラス名リスト枠(上段左端)に現われる
Object subclass: #BBFacility
    instanceVariableNames: 'limit loan shares'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BBLoanSyndicate-Data'

引き続き、BBFacility の定義を書き換えるかたちで BBLoan を定義します。

  • クラス名を BBLoan に変更し、インスタンス変数の limit loan を削除して shares のみ残し accept (alt + s)。
Object subclass: #BBLoan
    instanceVariableNames: 'shares'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BBLoanSyndicate-Data'

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate02.png


続いて、それぞれのクラスに暗黙に呼ばれる初期化メソッド(#initialize)を定義します。

  • クラス名リスト枠(上段右端)で BBFacility クラスをクリックして選択
  • メソッドカテゴリーリスト枠(上段右から三番目)で no messages をクリック
  • 次のメソッドを入力し、accept (alt + s) でコンパイル。イニシャルを尋ねられたら適当に入力して Accept (alt+s)。
initialize
    shares := Dictionary new

同様に BBLoan にも同じメソッドを定義します。

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate03.png


これでデータの定義は完了です。なお、Share、SharePie、Company などはコードの簡素化のため省きます。



▼ コンテキストの定義

データ同様、コンテキストオブジェクトのクラスをデータの時と同様の操作で定義します。

  • Context タブをクリックして切り替え
  • NameOfSubclass を BBLoanSyndicate に書き換え
  • instanceVariableNames: の引数である '' を 'facility' に書き換え
  • accept (alt + s) → BBLoanSyndicate が左上のクラス名リスト(上段左端の枠)に現われるのを確認

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate04.png

BB1Context subclass: #BBLoanSyndicate
    instanceVariableNames: 'facility'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BBLoanSyndicate-Context'


▼ データおよびコンテキストへのアクセッサーの追加

以下では簡単のため、データおよびコンテキストのインスタンス変数に対するアクセッサーメソッドが定義されていることを前提にしているので、このタイミングでこれらを自動生成しておきます。ワークスペース(デスクトップメニュー → open... → workspace)などで、次のコードを入力後に選択し、 do it (alt + d) してください。

| browser |
browser := Browser new.
{BBLoanSyndicate. BBFacility. BBLoan} do: [:class |
    browser setClass: class selector: nil; createInstVarAccessors]


▼ インタラクションの定義

コンテキストを指定して、そこでのロールとその振る舞いを定義します。ここまでの Smalltalk の伝統的なクラスブラウザの作法でのクラス定義と違い、ロールは楕円で示され、それらを矢印でつなぐことで相互関係を図形的に記述します。

  • Interaction タブをクリックして切り替え
  • BBLoanSyndicate コンテキストをクリックして選択
  • 右上の枠で右クリックして add role → Lender と入力 → Accept (alt + s)
  • Loan ロールを示す楕円を枠内の適当な場所をクリックして配置
  • 同様に、AmountPie、PercentagePie を作成して配置
  • Lender を右クリックして add link → AmountPie をクリック
  • AmountPie を右クリックして add link → PercentagePie をクリック

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate05.png

処理を簡潔に書きやすくするため、元エントリーとは関係をちょっと変えています。



▼ ロールの定義

各ロールにメソッドを定義します。

  • ロールをクリックして選択 → 下のコード枠にメソッドを記述 → alt + s で accept

定義するメソッドを次に示します。入力やコピペは冒頭の ロール名 >> を省いて行なってください。accept 時に、そのロールに対する最初のメソッド定義の場合は、「Role trait for Lender is not defined. Do you want to define it so that I can compile your method?」と尋ねられるので、Yes としてください。また、以下のコードにはいくつか未定義のメソッドが含まれています。コンパイラにその旨を警告されたときは、未定義のメソッド名をそのままポップアップから選択してください。

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate06.png

Lender >> draw: amount
    AmountPie increase: amount.
    self limit: self limit - amount
Lender >> pay: amount
    AmountPie decrease: amount.
    self limit: self limit + amount
AmountPie >> increase: amount
    PercentagePie shares associationsDo: [:assoc |
        | company percentage |
        company := assoc key.
        percentage := assoc value / 100 asFloat.
        self shares
            at: company
            put: (self shares at: company) + (amount * percentage)]
AmountPie >> decrease: amount
    | shares total |
    shares := self shares.
    total := shares sum asFloat.
    shares keysDo: [:company |
        | current |
        current := shares at: company.
        shares at: company put: current - (amount * (current / total))]


▼ ロールのデータへのバインド

  • Lender を右クリック → connect using class → BBFacility を選択
  • AmountPie を右クリック → connect using class → BBLoan を選択

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate07.png



▼ コンテキストにおけるロールへのデータマッピングメソッドの定義

  • Context タブをクリックして切り替え
  • BBLoanSyndicate をクリックして選択解除後、ふたたびクリックして選択
  • role mapping カテゴリーがメソッドカテゴリーリスト(左から三番目の枠)に追加されていることを確認
  • role mapping カテゴリーをクリックして選択
  • メソッド名リスト(右端の枠)から Lender を選択し、コード枠内のコードを次のように書き換えて accept (alt + s)。
Lender
    ^facility
  • 同様に AmountPie、PercentagePie も定義を次のように書き換え、accept (alt + s)。
AmountPie
    ^facility loan
PercentagePie
    ^facility

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate08.png



▼ コンテキストへの各種メソッドの定義

BBLoanSyndicate に初期化メソッド(#buildFacility、#joinFacility:percentage:)、および、トリガーメソッド(#draw: #pay:)を定義します。

  • カテゴリーリスト枠(上段三番目)で role binding が選択されていたらクリックして選択解除
  • 同じくカテゴリーリスト枠で右クリック → add category... → そのまま Accept (alt + s)。
  • 同じくカテゴリーリスト枠に triggers が現われるのでクリックして選択
  • 次のそれぞれのメソッド定義を下段コード枠にペーストして accept (alt + s)。
buildFacility
    facility := BBFacility new.
    facility loan: BBLoan new.
joinFacility: company percentage: int
    facility shares at: company put: int.
    facility loan shares at: company put: 0
draw: amount
    self executeInContext: [(self at: #Lender) draw: amount]
pay: amount
    self executeInContext: [(self at: #Lender) pay: amount]

http://squab.no-ip.com/collab/uploads/61/BBLoanSyndicate09.png



▼ 動作チェック

以上で定義は完了です。実際に動作を確認してみましょう。アクセッサーの定義と同様にワークスペースなどで次のコードを入力後、選択して、do it (alt + d) すると、トランスクリプトが開いて結果が出力されます。

| syndicate |
syndicate := BBLoanSyndicate new buildFacility.
syndicate joinFacility: #A percentage: 50.
syndicate joinFacility: #B percentage: 30.
syndicate joinFacility: #C percentage: 20.
syndicate facility limit: 100000.

syndicate draw: 10000.
syndicate pay: 5000.

World findATranscript: nil.
syndicate facility loan shares associations sort do: [:assoc |
    Transcript cr; show: assoc]
[2669]#A->2500.0
[3763]#B->1500.0
[1129]#C->1000.0

と、まあ、いちおう動きはするのですが、じつはまだ #executeInContext: をコンテキスト内のメソッドで書かなければならない理由や self at: #Role を self Role と書いてはいけない理由などモヤっとしているところも多々あるので、DCI デザインを含め、まだまだ勉強が必要なことを痛感しました。^^;

 
2004 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 06 | 07 |

最近のコメント

1. 07/26 sumim
2. 07/26 nqthm
3. 06/21 nqthm
4. 06/21 sumim
5. 06/21 nqthm

最近のトラックバック

1. 11/16 Shaped memorandum - 2010年07月27日のツイート
2. 04/15 みかままの覚書分館(リハビリ中) - 2010年07月14日のツイート
3. 06/21 Twitter / @rubyist_bot
4. 06/21 Twitter / @sumim
5. 06/21 I like Ruby too. - オライリー方面より

この日記のはてなブックマーク数
825689