Clojure,F#和Scala中的保龄球Kata

在我们晚上的一次学徒会议上,一对正在用Java做Bowling Kata 。 复习他们的代码后,我认为最好自己动手做。

Codurance的每个工匠都是多语种开发人员,尽管我们的价值观非常相似,但在编程语言和编码风格方面,我们都有自己的偏好。 可以想像,我们不能总是避免对我们不太喜欢的所有语言开一两个玩笑,但公司中的其他工匠却喜欢。 因此,仅出于娱乐目的,我们中的许多人决定使用我们选择的语言来制作相同的kata。 很高兴看到使用不同语言解决的同一问题。 尽管仍然有一些工匠和学徒用不同的语言来解决kata问题,但到目前为止,这里是我最喜欢的三种解决方案(不分先后顺序):

Clojure(作者Mashooq)

(ns bowling.core-test
  (:require [clojure.test :refer :all]
            [bowling.core :refer :all]))

(deftest bowling 
  (testing "strikes for all rolls"
    (is (= 300 (score "XXXXXXXXXXXX"))))

  (testing "normal scores"
    (is (= 99 (score "91919393929291219191"))))

  (testing "normal scores or misses"
    (is (= 90 (score "9-9-9-9-9-9-9-9-9-9-")))
    (is (= 93 (score "919-9-9-9-9-929-9-9-"))))

  (testing "mixture of stikes and normals"
    (is (= 98 (score "9-X8-9-9-9-9-9-9-9-")))
    (is (= 104 (score "9-X8-9-9-9-9-9-9-X23")))
    (is (= 28 (score "--X81--------------")))
    (is (= 27 (score "--X8-1-------------"))))

  (testing "spares for all rolls"
    (is (= 150 (score "5/5/5/5/5/5/5/5/5/5/5"))))

  (testing "mixture of spares and normals"
    (is (= 82 (score "9-8/--9-9-9-9-9-9-9-")))
    (is (= 84 (score "9-8/--9-9-9-9-9-9-9/1")))
    (is (= 12 (score "--8/1---------------")))
    (is (= 11 (score "--8/-1--------------")))))
(ns bowling.core)

(defn- spare?[s] (= \/ s))
(defn- strike? [s] (= \X s))
(defn- spare-or-strike? [s] (or (spare? s) (strike? s)))
(defn- miss? [s] (or (= nil s) (= \- s)))

(defn- score-for [s] 
  (cond 
     (spare-or-strike? s)  10
     (miss? s)  0 
     :else (read-string (str s))))

(defn- score-roll [this-roll rem-rolls]
  (cond 
    (strike? this-roll) (+ 10 (score-for (first rem-rolls)) (score-for (first (rest rem-rolls))))
    (spare? this-roll) (+ 10 (score-for (first rem-rolls)))
    (spare? (first rem-rolls)) 0
    :else (score-for this-roll)))

(defn- score-rolls [acc rolls]
  (if (seq rolls)  
    (let [running-score (+ acc (score-roll (first rolls) (rest rolls)))]
      (score-rolls running-score (rest rolls)))
    acc))

(defn- expand-strikes [rolls]
  (seq (reduce str  (map #(if  (strike? %) "X-"  (str %)) (seq rolls)))))

(defn- deduct-extra-rolls [score rolls]
  (- score  (score-rolls 0 (drop 20 (expand-strikes rolls)))))

(defn score [rolls] 
  (deduct-extra-rolls (score-rolls 0 (seq rolls)) rolls))

Mash的GitHub上查看

F#(Pedro撰写)

namespace BowlingV2.FSharpKatas

    module Bowling = 
        open System

        type private Rolls = Strike | Spare | Roll
        type private Pins = Pins of int
        type private Roll = Rolls * Pins

        let private maxRolls = 20
        let private maxPins = 10
        let private noPins = 0

        let private pinCountForRoll roll =
            let (Pins pins) = snd roll
            pins

        let private pinsFromRawRoll rawRoll =
            Pins (Int32.Parse(rawRoll.ToString()))

        let private sparePinsFromRawRoll rawRoll = 
            Pins (maxPins - Int32.Parse(rawRoll.ToString()))

        let private parse roll index rolls =
            let previousRoll = fun () -> Seq.item (index - 1) rolls
            match roll with
            | '-' -> Roll, Pins noPins
            | '/' -> Spare, sparePinsFromRawRoll(previousRoll())
            | 'X' -> Strike, Pins maxPins
            | r -> Roll, pinsFromRawRoll r

        let private scoreRoll index rolls =
            let bonusRoll = fun(lookAhead) ->  
                if index + lookAhead < Seq.length rolls 
                then pinCountForRoll (Seq.item (index + lookAhead) rolls) 
                else noPins       

            let exceedsMaxRolls = fun() ->
                rolls 
                |> Seq.take index
                |> Seq.map (fun r -> match r with | (Strike, _) -> 2 | _ -> 1)
                |> Seq.sum >= maxRolls

            match Seq.item index rolls with
                | (_, _) when exceedsMaxRolls() -> noPins
                | (Spare, Pins pins) -> pins + bonusRoll 1
                | (Strike, Pins pins) -> pins + bonusRoll 1 + bonusRoll 2
                | (Roll, Pins pins) -> pins

        let scoreGame rolls =
            let parsedRolls = rolls |> Seq.mapi (fun index roll -> 
                                                    parse roll index rolls)

            parsedRolls
            |> Seq.mapi (fun index _ -> scoreRoll index parsedRolls)
            |> Seq.sum

    module BowlingTests =
        open NUnit.Framework
        open Swensen.Unquote
        open Bowling

        [<Test>]
        let ``calculate scores with no strikes or spares``() =
            test <@ scoreGame "--" = 0 @> 
            test <@ scoreGame "1" = 1 @>
            test <@ scoreGame "13" = 4 @>
            test <@ scoreGame "13521" = 12 @>

        [<Test>]
        let ``calculate scores containing a miss``() =
            test <@ scoreGame "1-5-" = 6 @>
            test <@ scoreGame "9-9-9-9-9-9-9-9-9-9-" = 90 @>

        [<Test>]
        let ``calculate scores containing spares``() =
            test <@ scoreGame "1/" = 10 @>
            test <@ scoreGame "1/--" = 10 @>
            test <@ scoreGame "1/-5" = 15 @>
            test <@ scoreGame "1/35-" = 21 @>
            test <@ scoreGame "1/3/23" = 30 @>
            test <@ scoreGame "5/5/5/5/5/5/5/5/5/5/5" = 150 @>

        [<Test>]
        let ``calculate scores containing strikes``() =
            test <@ scoreGame "X" = 10 @>
            test <@ scoreGame "X--" = 10 @>
            test <@ scoreGame "X--51" = 16 @>
            test <@ scoreGame "X51" = 22 @>
            test <@ scoreGame "XXXXXXXXXXXX" = 300 @>
            test <@ scoreGame "XXXXXXXXXX12" = 274 @>
            test <@ scoreGame "1/35XXX45" = 103 @>
            test <@ scoreGame "1/35XXX458/X35" = 149 @>
            test <@ scoreGame "1/35XXX458/X3/" = 153 @>
            test <@ scoreGame "1/35XXX458/X3/23" = 160 @>
            test <@ scoreGame "1/35XXX458/X3/X" = 173 @>
            test <@ scoreGame "1/35XXX458/X3/XX6" = 189 @>

Pedro的GitHub上查看

斯卡拉(Sandro)

package com.codurance.bowlingkata.full_scoring

import com.codurance.UnitSpec
import com.codurance.bowlingkata.full_scoring.BowlingFullScoreCalculator.scoreFor

class BowlingFullScoreCalculatorShould extends UnitSpec {

    "calculate scores with no strikes or spares" in {
        scoreFor("11111111112222222222") should be (30)
    }

    "calculate scores containing a miss" in {
        scoreFor("--------------------") should be (0)
        scoreFor("1-1----------------1") should be (3)
        scoreFor("9-9-9-9-9-9-9-9-9-9-") should be (90)
    }

    "calculate scores containing spares" in {
        scoreFor("5/11------------3/11") should be (26)
        scoreFor("5/5/5/5/5/5/5/5/5/5/5") should be (150)
    }

    "calculate scores containing strikes" in {
        scoreFor("XXXXXXXXXXXX") should be(300)
        scoreFor("XXXXXXXXXX12") should be(274)
        scoreFor("1/35XXX458/X3/23") should be(160)
        scoreFor("1/35XXX458/X3/XX6") should be(189)
    }
}
package com.codurance.bowlingkata.full_scoring

object BowlingFullScoreCalculator {

    def scoreFor(rolls: String): Int = totalScore(rolls.split("").toList)

    private def totalScore(rolls: List[String], index: Int = 0, score: Int = 0): Int = {
        lazy val MISS  = "-"
        lazy val SPARE = ("/", () => 10 - rollScoreAt(index - 1) + if_(index < 19, rollScoreAt(index + 1)))
        lazy val STRIKE = ("X", () => 10 + if_(index + numberOfPreviousStrikes() < 18,
                                               rollScoreAt(index + 1) + rollScoreAt(index + 2)))

        def numberOfPreviousStrikes() = rolls.mkString.take(index).count(_ == 'X')

        def rollScoreAt(index: Int): Int =
            rolls(index) match {
                case STRIKE._1 => 10
                case SPARE._1  => 10 - rolls(index - 1).toInt
                case MISS      => 0
                case pins      => pins.toInt
            }

        rolls.drop(index) match {
            case STRIKE._1 :: _ => totalScore(rolls, index + 1, score + STRIKE._2())
            case SPARE._1 :: _  => totalScore(rolls, index + 1, score + SPARE._2())
            case MISS :: _      => totalScore(rolls, index + 1, score)
            case n :: _         => totalScore(rolls, index + 1, score + n.toInt)
            case List()         => score
        }
    }

    private def if_(condition: Boolean, ifTrue: => Int): Int = if (condition) ifTrue else 0
}

Sandro的GitHub上查看

乐趣,热情和尊重

在工作中获得乐趣,被充满激情和才华的工匠所包围,我们彼此之间的尊重以及学习和分享的意愿,是我对Codurance文化最钟爱的一些东西。 最初是用kata练习的学徒,后来变成了在工匠和学徒之间学习和共享知识的好方法。 我们的一些工匠和学徒也在Kotlin,Haskell,Java和C#中致力于他们的解决方案。

在我们当中,我们可能永远不会同意我们更喜欢哪一种,我们会让您选择最喜欢的一种。 :)

感谢MashPedro的Clojure和F#实现。

翻译自: https://www.javacodegeeks.com/2016/05/bowling-kata-clojure-f-scala.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值