文字の配列を文字列にする
Ruby と違って Smalltalk の文字列(a String)は要素がすべて文字(a Character)の特殊なコレクションです。ただ、文字のみからなる配列を文字列にしたいとき、
#($a $b $c) asString
だと、
"=> '#($a $b $c)' "
というように、元の配列の文字列表現になってしまうので駄目じゃん…と思っていたのですね。でも、#as: というのがありました。
#($a $b $c) as: String
"=> 'abc' "
ぐっど、ぐっど。
追記:
もちろん、正統派ならはじめからこうします。
String withAll: #($a $b $c) " => 'abc' "
あくまで、注目している文字のコレクションをレシーバにしたいとき、の話です。
n 番目の組み合わせ
id:sumim:20041206#p1 の続き。c. も比較的、簡単そうなのでトライしてみました。
target chars |
=> "%'[03569FfLoUXy
定義は、
SequenceableCollection >> combinations: digits atATimeAt: target | sum cursor numOfCombs resultDigit | sum _ 0. cursor _ 1. numOfCombs _ (self size - cursor) take: digits - 1. [(sum + numOfCombs) < target] whileTrue: [ sum _ sum + numOfCombs. cursor _ cursor + 1. numOfCombs _ (self size - cursor) take: digits - 1]. resultDigit _ self species with: (self at: cursor). ^ resultDigit, ((digits - 1) > 0 ifTrue: [(self allButFirst: cursor) combinations: digits - 1 atATimeAt: target - sum] ifFalse: [self species new])
Integer >> #take: は、レシーバが n、パラメータが r のときの組み合わせの数 n C r を返すメソッドです。
続: n 番目の組み合わせ
ループを排除して、心持ち、すっきりとさせてみました。心持ち。w
SequenceableCollection >> combinations: digits atATimeAt: target | size sum ranges cursor digit | size _ self size. (size < digits or: [digits < 1]) ifTrue: [^ self species new]. ranges _ OrderedCollection new. (size - 1 to: digits - 1 by: -1) inject: 0 into: [: lastIndex : n | ranges add: lastIndex + (n take: digits - 1)]. cursor _ ranges findFirst: [: lastIndex | lastIndex >= target]. sum _ cursor > 1 ifTrue: [ranges at: cursor - 1] ifFalse: [0]. digit _ self species with: (self at: cursor). ^ digit, ((self allButFirst: cursor) combinations: digits - 1 atATimeAt: target - sum)
Shiro さんの組み合わせの数版を Smalltalk で
組み合わせの数を使うとこんな感じ?
(let loop ((chars (string->list *chars*)) (str '()) (num *value*)) (if (= (length str) 16) (list->string (reverse str)) (let ((x (c (- (length chars) 1) (- 15 (length str))))) (if (> x num) (loop (cdr chars) (cons (car chars) str) num) (loop (cdr chars) str (- num x)))))) => "\"%'[03569FfLoUXy"
これを、意味を持つ範囲で(reverse とかは真似てもしかたがないので…)できるだけ忠実に Squeak システムの Smalltalk 言語に訳すとこんなかんじ。
SequenceableCollection >> loopWith: str with: num | chars x | chars _ self. str size = 16 ifTrue: [^ str as: String]. x _ chars size - 1 take: 15 - str size. ^ x > num ifTrue: [chars allButFirst loopWith: (str copyWith: chars first) with: num] ifFalse: [chars allButFirst loopWith: str with: num - x]
value chars |
かないません。w orz
続々: n 番目の組み合わせ
Shiro さん版を受けて、SequenceableCollection >> #combinations:atATimeAt: を改良してみました。
SequenceableCollection >> combinations: digits atATimeAt: target | size lastIndex | size _ self size. (size < digits or: [digits < 1]) ifTrue: [^ self species new]. lastIndex _ size - 1 take: digits - 1. ^ lastIndex >= target ifTrue: [(self first: 1), (self allButFirst combinations: digits - 1 atATimeAt: target)] ifFalse: [self allButFirst combinations: digits atATimeAt: target - lastIndex]
最初っからこう書ければ、かっこいいんですけどねぇ…。orz