TDD Kata 05 – Karate Chop

Dies ist mein wöchentlicher Kata Post. Lies den ersten um zu erfahren, worum es hier geht.

Letzte Woche: Word Wrap

In PHP/Behat habe ich “scenario outlines” kennen gelernt, die viel von den Wiederholungen in Feature Dateien beseitigen.

Vollständiges Ergebnis:

Feature: Word Wrap
  In order to display text on tiny screens
  I want to wrap lines of a long text

  Scenario Outline: Wrap text
    When I wrap <text> at <col> columns
    Then I should retrieve the wrapped string <wrapped>

    Examples:
      | text  | col   | wrapped |
      |     | 1   |       |
      | word  | 2   | wo\nrd |
      | word  | 3   | wor\nd |
      | word  | 4   | word |
      | word  | 5   | word |
      | word word | 4 | word\nword |
      | word word | 5 | word\nword |
      | word word | 7 | word\nword |
      | word word | 8 | word\nword |
      | word word | 9 | word word |
      | abcdefg hij klmno p qurstuvwx yz | 4 | abcd\nefg\nhij\nklmn\no p\nqurs\ntuvw\nx yz |

<?php
use Behat\Behat\Context\Context;

class WordwrapContext implements Context
{
    private $result;

    /**
     * @When /^I wrap (.*) at (.*) columns$/
     */
    public function iWrapAtColumns($text, $col)
    {
        $this->result = wrap($text, $col);
    }

    /**
     * @Then /^I should retrieve the wrapped string (.*)$/
     */
    public function iShouldRetrieveTheWrappedString($wrapped)
    {
        $this->assertEquals(str_replace('\n', "\n", $wrapped), $this->result);
    }

    private function assertEquals($expected, $actual)
    {
        if (gettype($expected) != gettype($actual)) {
            throw new LogicException("Expected type " . gettype($expected). ", got " . gettype($actual));
        }
        if ($expected !== $actual) {
            throw new LogicException("Expected " . var_export($expected, true) . ", got " . var_export($actual, true));
        }
    }
}

function wrap(string $text, int $columnSize): string
{
    if (strlen($text) <= $columnSize) {
        return $text;
    }
    $splitPosition = min(strrpos($text, ' '), $columnSize) ?: $columnSize;
    return trim(substr($text, 0, $splitPosition)) . "\n" . wrap(trim(substr($text, $splitPosition)), $columnSize);
}

In Ruby habe ich durch die Kata String Indexing kennengelernt. Es hat eine Weile gedauert, bis ich es wie beabsichtigt zum Laufen bekommen habe und das Ergebnis ist wahrscheinlich nicht sehr idiomatisch.

Vollständiges Ergebnis:

gem 'minitest', '~> 5.4'
require 'minitest/autorun'

class WordWrapTest < Minitest::Test

    def assert_wrap expected, text, columns
        assert_equal expected, wrap(text, columns)
    end

    def test_single_short_word
        assert_wrap "word", "word", 4
    end

    def test_single_long_word
        assert_wrap "long\nword", "longword", 4
    end

    def test_single_very_long_word
        assert_wrap "very\nlong\nword", "verylongword", 4
    end

    def test_space_before_wrap
        assert_wrap "two\nword\ns", "two words", 4
    end

    def test_space_after_wrap
        assert_wrap "long\nword", "long word", 4
    end

    def test_space_somewhere_else
        assert_wrap "abc\nde\nfghi\njk", "abc de fghi jk", 4
    end
end
    
def wrap text, columns
    if text.length <= columns then
        return text
    end
    break_at = text[0..columns-1].index(" ") || columns
    if text[break_at] == " " then
        line_start = break_at + 1
    else
        line_start = break_at
    end
    text[0..break_at-1] + "\n" + wrap(text[line_start..-1], columns)
end

Fünfte Kata: Karate Chop

Der gewöhnliche Name von “Karate Chop” ist “Binary Search”. Ein binärer Suchalgorithmus findet die Position eines Werts in einem sortierten Array von Werten. Die Kata ist auf http://codekata.com/kata/kata02-karate-chop/ beschrieben, wo man aufgefordert wird, fünf verschiedene Ansätze zu finden.

Meine persönlichen Ziele diese Woche

  • Fünf verschiedene Ansätze finden
  • Verschiedene Sprachen nutzen, wie es passt. Auf bestimmte Techniken möchte ich mich noch nicht festlegen.