あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

Pythonのrange関数をJavaScriptで再帰を使って実装してみたものをGaucheに移植してみた

今朝投稿した、Pythonのrange関数をJavaScriptで再帰を使って実装してみた - あと味の続き。

JavaScript版は基本的な関数で実装したので、おそらくGaucheScheme)にも簡単に移植できると思って取り組んでみたものの、正直、慣れてなさすぎて超大変でした。

対してコメントもできませんが、もうちょっとGaucheらしいスマートな実装方法もあるのかしら。

元になったJavaScriptのコード

function range(start, end, step) {
  return (
    (start === undefined) ?
      new Error('range expected at least 1 arguments, got 0') :
    (typeof start !== 'number') ?
      new Error('range() integer start argument expected, got ' + typeof start) :
    (end !== undefined && typeof end !== 'number') ?
      new Error('range() integer end argument expected, got ' + typeof end) :
    (step !== undefined && typeof step !== 'number') ?
      new Error('range() integer step argument expected, got ' + typeof step) :
    (step === 0) ?
      new Error('range() step argument must not be zero') :
    (arguments[3]) ?
      new Error('range expected at most 3 arguments, got ' + arguments.length) :

    (function(start, end, step, list) {
      return (
        (step > 0 && start < end || step < 0 && start > end) ?
          arguments.callee(start+step, end, step, list.concat(start)) :
        list
      );
    })(end === undefined ? 0 : start, end === undefined ? start : end, step === undefined ? 1 : step ,[])
  );
}

Gaucheのコード

(define (range start . args)
  (let-optionals* args ((end '())
                       (step '()))
  (define (range2 start end step lis)
    (cond [(or (and (> step 0) (< start end)) (and (< step 0) (> start end)))
            (range2 (+ start step) end step (append lis (cons start '())))]
          [else lis]))
  (cond [(eq? (number? start) #f) (error "range() integer start argument expected, got " (class-of start))]
        [(and (eq? (null? end) #f) (eq? (number? end) #f)) (error "range() integer end argument expected, got " (class-of end))]
        [(and (eq? (null? step) #f) (eq? (number? step) #f)) (error "range() integer step argument expected, got " (class-of step))]
        [(eq? step 0) (error "range() step argument must not be zero")]
        [(< 2 (length args)) (error "range expected at most 3 arguments, got " (+ 1 (length args)))]
        [else (range2 (if (null? end) 0 start)
                      (if (null? end) start end)
                      (if (null? step) 1 step)
                      '())])))

さっそく省略可能引数の扱い方がわからずハマりました。後、リストの構造がJavaScriptと違って再帰的なので、appendの引数の部分もハマりました。

その他は割とスムーズに移植できた気がします。JavaScript版に比べて前置記法が多少コードを見やすくしてくれている気もします。

Lispを使って、自分で考えた何かを作ったのは初めての経験なので、動いて感動した。