Kata — это кодовые головоломки, которые помогут вам отточить свои навыки.
Мне нравится создавать и решать кодовые ката. Код Ката — это головоломки по программированию, которые помогут вам отточить свои навыки программирования.
Я написал статью под названием «Learn to Kata and Kata to Learn» для книги «97 вещей, которые должен знать каждый Java-программист», и ссылка на статью доступна бесплатно здесь, в публикации на Medium.
Удивительный мир Wordle
Wordle — это очень популярная онлайн игра головоломка, в которой у вас есть шесть шансов угадать слово из пяти букв.
Каждое предположение приводит к изучению того, какие символы соответствуют символам в слове. Вам даются подсказки с помощью цветов, которые сообщают, правильно ли вы вставили букву в правильное положение, если буква находится в слове, но в неправильном положении, или если буква не соответствует какой-либо букве в слове.
Вы можете узнать больше об увлечении Wordle в этой статье.
Wordle explained: Everything you need to know to master the viral word game
Wordle — это вирусная словесная игра, недавно приобретенная New York Times за несколько миллионов. Но…
JLDD = Jet Lag Driven Development
В прежние времена, когда технические конференции и путешествия по миру были обычным явлением, несколько Java чемпионов(Хосе Помар, Нихил Нанивадекар, и я) делились задачами по кодированию в Твиттере, пока мы были на конференциях (обычно JavaOne/Oracle CodeOne).
У всех нас была разная степень смены часовых поясов во время конференций, поэтому мы решили использовать хэштег JLDD (разработка, управляемая сменой часовых поясов), когда публиковали задачи по программированию.
Чаще всего задачи программирования были направлены на решение проблем с Java-коллекциями или потоками. Обычно я размещал решения, используя Eclipse Collections.
На протяжении всей пандемии мы время от времени делились друг с другом проблемами JLDD в Твиттере, несмотря на то что джетлаг уже давно прекратился. Несколько недель назад я поделился Haiku Kata, используя Java Text Blocks и Eclipse Collections.
Haiku на Java с использованием текстовых блоков
Творческое письмо в сочетании с текстовыми блоками Java и коллекциями Eclipse
Хосе Помар затем сделал все возможное и закодировал задачу JLDD в прямом эфире в 25-минутном видео JEP Cafe # 9. Он проделывает потрясающую работу, объясняя решения Eclipse Collections и Java 17. Отличная работа!
Wordle Ката
Хосе Помар на этой неделе мне прислали вызов Wordle Kata JLDD в виде теста, для которого мне нужно было написать код, проходящий этот тест. Мне нравится этот тип ката, который следует классическому стилю TDD с использованием подхода «сначала тест». Выше приведен тестовый код для kata с использованием простых утверждений JUnit 5.
Правила, основанные на этом тесте, довольно просты.
Если буква в строке предположения не соответствует букве в скрытом слове, замените символ в выводе на «.»
Если буква в строке предположения совпадает с буквой в скрытом слове и буква находится в той же позиции, то замените символ на заглавную букву.
Если буква в строке предположения совпадает с буквой в скрытом слове, но буква находится в другой позиции, то замените символ строчной буквой.
Если буква совпадает, но появляется в угаданной строке больше раз, чем в скрытом слове, то замените дополнительные символы в выводе на «.».
Мое первое решение с использованием Eclipse Collections
Решение, которое я придумал, выглядело следующим образом и прошло все тесты.
Код принимает предположение, сравнивает каждую букву со скрытым словом и, если есть прямое совпадение, печатает заглавную букву, в противном случае печатает строчную букву, если есть косвенное совпадение или «.» если нет совпадения или буква является дополнительным совпадающим символом.
Метод collectWithIndex
типа CharAdapter
преобразует строку guessChars
в один символ за раз. Сначала я думал, что никакие char
значения не будут упакованы как объекты Character
, но оказалось, что я ошибался.
Метод collectWithIndex
принимает CharIntToObjectFunction
, что означает, что значения char
для каждого выходного символа будут упакованы как объекты Character
.
Это также означает, что у нас нет чисто примитивной версии collectWithIndex
в Eclipse Collections, как в случае с collectChar
. Я думаю, что это, вероятно, приемлемо в большинстве случаев и не должно быть слишком дорогостоящим для этого конкретного сценария использования.
Я не думаю, что добавление чисто примитивной версии с именем collectCharWithIndex
имело бы смысл.
Однако существует более серьезная проблема, чем упаковка char
значений в виде Character
объектов перед созданием выходных данных String
. Я обнаружил, что отсутствует тестовый пример и дополнительное правило, которое мы должны добавить в ката.
Правило: отдавайте предпочтение прямым совпадениям, а не косвенным.
Я добавил следующий тестовый пример, который приводит к сбою моего первого решения.
Позвольте мне увеличить масштаб, чтобы вы могли видеть тестовый пример более четко.
В этом случае буква «а» будет иметь прямое совпадение в третьей позиции, но косвенные совпадения в догадке следует игнорировать в пользу прямого совпадения в третьей позиции.
Мое обновленное решение
Для тех, кто меня знает, вы должны знать, что я несколько одержим обеспечением хорошей симметрии в API Eclipse Collection.
В этом конкретном сценарии использования было бы идеально, если бы существовал эквивалент selectWithIndex
и rejectWithIndex
, имеющихся для примитивных типов коллекций в Eclipse Collections. Этот конкретный вариант использования может заставить меня согласиться и добавить недостающие методы.
Однако существует альтернативный метод, который я могу использовать для реализации эквивалента этих двух методов. Это метод injectIntoWithIndex
.
Вот мое обновленное решение, использующее injectIntoWithIndex
для создания CharBag
с оставшимися символами, которые не имеют прямых совпадений.
Если вы хотите понять, как метод injectIntoWithIndex
работает, вы можете прочитать следующий блог про injectInto (EC by Example: InjectInto). Метод injectInto можно использовать для реализации большинства шаблонов итерации, что также относится к injectIntoWithIndex
. Это одновременно магические и мощные методы.
Обновление: альтернативное решение с использованием zipChar
Иногда, когда вы работаете над одним и тем же кодом в течение 18 лет, вы кое-что забываете. К счастью, друг будет время от времени напоминать вам о вещах, которые вы, возможно, забыли. Это случилось на этой неделе, когда Vladimir Zakharov поделился другим решением для Wordle JLDD Kata в Твиттере, используя метод с именем zipChar
.
Решение Влада довольно крутое, и это определенно не тот подход, о котором я бы подумал. Я забыл, что zip
в Eclipse Collections была доступна примитивная версия. Каждый примитивный тип OrderedIterable
в Eclipse Collections поддерживает zip
одного и того же типа. Итак, у a CharAdapter
есть метод zipChar
.
Вскоре я вспомнил, что был блог, который я написал более четырех лет назад о примитиве zip
.
Как только я увидел решение Влада, я понял, что могу написать альтернативу своему собственному решению, используя zipChar
для замены injectIntoIndex
и collectWithIndex
.
Два решения, которые используют zipChar
, существенно различаются по алгоритму, который они используют для вычисления окончательного результата строки предположения.
Исходник моего финального решения с Eclipse Collections
Я создал свое окончательное решение задачи Wordle Kata JLDD с использованием коллекций Eclipse. Я с нетерпением жду возможности увидеть решение на чистой Java 17 от Хосе Помар.
. Я всегда узнаю от Хосе что-то новое и интересное о Java, чего раньше не знал.
import org.eclipse.collections.api.bag.primitive.MutableCharBag;
import org.eclipse.collections.impl.factory.Strings;
import org.eclipse.collections.impl.factory.primitive.CharBags;
import org.eclipse.collections.impl.string.immutable.CharAdapter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class WordleTest
{
@Test
public void wordleTest()
{
Assertions.assertEquals(".....", new Wordle("aaaaa").guess("bbbbb"));
Assertions.assertEquals("A....", new Wordle("aaaaa").guess("abbbb"));
Assertions.assertEquals(".A...", new Wordle("aaaaa").guess("babbb"));
Assertions.assertEquals("..A..", new Wordle("aaaaa").guess("bbabb"));
Assertions.assertEquals("...A.", new Wordle("aaaaa").guess("bbbab"));
Assertions.assertEquals("....A", new Wordle("aaaaa").guess("bbbba"));
Assertions.assertEquals(".a...", new Wordle("abbbb").guess("caccc"));
Assertions.assertEquals("..a..", new Wordle("abbbb").guess("ccacc"));
Assertions.assertEquals("...a.", new Wordle("abbbb").guess("cccac"));
Assertions.assertEquals("....a", new Wordle("abbbb").guess("cccca"));
Assertions.assertEquals("A....", new Wordle("abbbb").guess("accca"));
Assertions.assertEquals("A....", new Wordle("abbbb").guess("accaa"));
Assertions.assertEquals("A..a.", new Wordle("aabbb").guess("accaa"));
Assertions.assertEquals("AA...", new Wordle("aabbb").guess("aacaa"));
Assertions.assertEquals("...aa", new Wordle("aabbb").guess("cccaa"));
Assertions.assertEquals("..A..", new Wordle("bbabb").guess("aaaaa"));
Assertions.assertEquals("AAAAA", new Wordle("aaaaa").guess("aaaaa"));
Assertions.assertEquals("BRAVO", new Wordle("bravo").guess("bravo"));
}
record Wordle(String string)
{
Wordle(String string)
{
this.string = string.toLowerCase();
}
public String guess(String guess)
{
CharAdapter guessChars = Strings.asChars(guess.toLowerCase());
CharAdapter hiddenChars = Strings.asChars(this.string);
MutableCharBag remaining = hiddenChars
.injectIntoWithIndex(
CharBags.mutable.empty(),
(bag, each, i) -> guessChars.get(i) != each ? bag.with(each) : bag);
return guessChars.collectWithIndex((each, i) -> hiddenChars.get(i) == each ?
Character.toUpperCase(each) : this.replaceDifferentPositionOrNoMatch(remaining, each))
.makeString("");
}
private char replaceDifferentPositionOrNoMatch(MutableCharBag remaining, char each)
{
return remaining.remove(each) ? each : '.';
}
}
}
Заключительные соображения
Надеюсь, вам понравился этот блог о моих решениях задачи JLDD Kata с использованием коллекций Eclipse. Я, конечно, был бы рад видеть другие решения этих задач, чтобы мы все могли изучить новые и разные решения задач программирования. Эти решения могут быть на Java с использованием ваших любимых библиотек или даже на другом языке программирования.
Спасибо, что нашли время прочитать!