Следующий код работает так, как мне нужно, но это уродливо, излишне или рядом других вещей. Я посмотрел на формулы и попытался написать несколько решений, но в итоге я получил такое же количество утверждений.
Есть ли математическая формула, которая принесет мне пользу в этом случае, или 16, если утверждения приемлемы?
Чтобы объяснить код, это для своего рода игры, основанной на пошаговой одновременности. У двух игроков по четыре кнопки действий каждая, и результаты получаются из массива (0-3), но переменные «один» и «два» могут быть назначил что угодно, если это поможет. В результате 0 = ни одного выигрыша, 1 = p1 побед, 2 = p2 побед, 3 = оба выигрыша.
public int fightMath(int one, int two) {
if(one == 0 && two == 0) { result = 0; }
else if(one == 0 && two == 1) { result = 0; }
else if(one == 0 && two == 2) { result = 1; }
else if(one == 0 && two == 3) { result = 2; }
else if(one == 1 && two == 0) { result = 0; }
else if(one == 1 && two == 1) { result = 0; }
else if(one == 1 && two == 2) { result = 2; }
else if(one == 1 && two == 3) { result = 1; }
else if(one == 2 && two == 0) { result = 2; }
else if(one == 2 && two == 1) { result = 1; }
else if(one == 2 && two == 2) { result = 3; }
else if(one == 2 && two == 3) { result = 3; }
else if(one == 3 && two == 0) { result = 1; }
else if(one == 3 && two == 1) { result = 2; }
else if(one == 3 && two == 2) { result = 3; }
else if(one == 3 && two == 3) { result = 3; }
return result;
}
if-statement
math
formula
TomFirth
источник
источник
f(a, b)
которая дает ответ в общем случае? Вы не объяснили логику расчета, поэтому все ответы - просто помада на свинье. Я бы начал с серьезного переосмысления логики вашей программы, использованиеint
флагов для действий очень устарело.enum
Они могут содержать логику и описательны, это позволит вам писать свой код более современным способом.Ответы:
Если вы не можете придумать формулу, вы можете использовать таблицу для такого ограниченного числа результатов:
источник
IndexOutOfBoundsException
равно выдаст, если один или несколько индексов выходят за пределы.i
/j
/k
для переменных цикла), именованные константы, упорядочение кода для чтения и т. Д., Но когда имена переменных и функций начинают занимать более 20 символов каждый из них, на мой взгляд, приводит к менее читаемому коду. Мой обычный подход состоит в том, чтобы попытаться найти понятный, но лаконичный код с комментариями здесь и там, чтобы объяснить, почему код структурирован таким, какой он есть (а не как). Помещение почему в именах просто загромождает все.Поскольку ваш набор данных очень мал, вы можете сжать все в 1 длинное целое и превратить его в формулу
Более побитовый вариант:
Это использует тот факт, что все кратно 2
Происхождение магической константы
Что я могу сказать? Мир нуждается в магии, иногда возможность чего-то требует его создания.
Суть функции, решающей задачу ОП, состоит в отображении из 2 чисел (одного, двух) области {0,1,2,3} в диапазон {0,1,2,3}. Каждый из ответов подошел, как реализовать эту карту.
Кроме того, в ряде ответов вы можете увидеть повторение проблемы в виде карты из 1 2-значного основания 4 числа N (один, два), где один - это цифра 1, два - это цифра 2, а N = 4 * один + два; N = {0,1,2, ..., 15} - шестнадцать разных значений, это важно. Выходные данные функции представляют собой однозначное основание 4 числа {0,1,2,3} - 4 различных значения, также важно.
Теперь однозначное число основания 4 может быть выражено как двузначное число основания 2; {0,1,2,3} = {00,01,10,11}, и поэтому каждый выход может быть закодирован только с 2 битами. Сверху возможно только 16 различных выходов, поэтому 16 * 2 = 32 бита - это все, что необходимо для кодирования всей карты; все это может вписаться в 1 целое число.
Константа M является кодированием карты m, где m (0) кодируется в битах M [0: 1], m (1) кодируется в битах M [2: 3], а m (n) кодируется в битах. М [п * 2: п * 2 + 1].
Осталось только проиндексировать и вернуть правую часть константы, в этом случае вы можете сдвинуть M вправо 2 * N раз и взять 2 младших значащих бита, то есть (M >> 2 * N) & 0x3. Выражения (one << 3) и (two << 1) просто умножают вещи, отмечая, что 2 * x = x << 1 и 8 * x = x << 3.
источник
Мне не нравится ни одно из представленных решений, кроме JAB. Ни один из других не позволяет легко читать код и понимать, что вычисляется .
Вот как я написал бы этот код - я знаю только C #, а не Java, но вы получите картину:
Теперь стало гораздо яснее, что здесь вычисляется: это подчеркивает, что мы вычисляем, кого атакует какая-то атака, и возвращаем оба результата.
Однако это может быть даже лучше; этот логический массив несколько непрозрачен. Мне нравится подход поиска по таблице, но я был бы склонен написать его так, чтобы было ясно, какова была предполагаемая семантика игры. То есть вместо того, чтобы «атака с нулем и защита одного не приводит к попаданию», вместо этого найдите способ сделать код более четко подразумевающим «атака с низким ударом и защита с низким блоком не приводит к попаданию». Сделайте так, чтобы код отражал бизнес-логику игры.
источник
Вы можете создать матрицу, которая содержит результаты
Когда вы хотите получить значение, вы будете использовать
источник
Другие люди уже предложили мою первоначальную идею, матричный метод, но в дополнение к консолидации операторов if вы можете избежать того, что у вас есть, убедившись, что предоставленные аргументы находятся в ожидаемом диапазоне и используя возвраты на месте (некоторое кодирование стандарты, которые я видел, применяют единую точку выхода для функций, но я обнаружил, что множественные возвраты очень полезны для избежания кодирования стрелок, и с преобладанием исключений в Java нет особого смысла строго соблюдать такое правило в любом случае поскольку любое неперехваченное исключение, выброшенное внутри метода, в любом случае является возможной точкой выхода). Возможно вложение операторов switch, но для небольшого диапазона значений, которые вы здесь проверяете, я нахожу, что операторы будут более компактными и вряд ли приведут к значительной разнице в производительности,
Это оказывается менее читабельным, чем могло бы быть из-за неправильности частей отображения ввода-> результата. Вместо этого я предпочитаю стиль матрицы из-за его простоты и того, как вы можете настроить матрицу так, чтобы она имела смысл визуально (хотя это частично зависит от моих воспоминаний о картах Карно):
Обновление: учитывая ваше упоминание о блокировке / ударе, вот более радикальное изменение в функции, которая использует перечисляемые типы, имеющие свойства / атрибуты, для входных данных и результата, а также немного изменяет результат для учета блокирования, что должно привести к более читаемая функция.
Вам даже не нужно менять саму функцию, если вы хотите добавить блоки / атаки большей высоты, только перечисления; добавление дополнительных типов ходов, вероятно, потребует модификации функции. Кроме того,
EnumSet
s может быть более расширяемым, чем использование дополнительных перечислений в качестве свойств основного перечисления, например,EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
и тогда,attacks.contains(move)
а неmove.type == MoveType.ATTACK
, хотя использованиеEnumSet
s, вероятно, будет немного медленнее, чем прямые проверки на равенство.Для случая, когда успешный блок приводит к счетчику, вы можете заменить
if (one.height == two.height) return LandedHit.NEITHER;
наКроме того, замена некоторых
if
операторов с использованием тернарного оператора (boolean_expression ? result_if_true : result_if_false
) может сделать код более компактным (например, код в предыдущем блоке станетreturn one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), но это может привести к затруднению чтения однострочников, поэтому я бы не стал Я рекомендую его для более сложных ветвлений.источник
one
иtwo
быть повторно использованным в качестве начальных точек моей таблицы спрайтов. Хотя для этого не требуется много дополнительного кода.EnumMap
класс, который вы можете использовать для отображения перечислений на ваши целочисленные смещения (вы также можете использовать порядковые значения членов перечисления напрямую, например,Move.ATTACK_HIGH.ordinal()
было бы0
,Move.ATTACK_LOW.ordinal()
будет1
и т. Д., Но это более хрупко / менее гибко, чем явно ассоциирование каждого члена со значением, так как добавление перечислимых значений между существующими приведет к сбросу счетчика, что было бы не так сEnumMap
.)attack(against)
метод вMove
перечисление, возвращая HIT, если ход является успешной атакой, BACKFIRE, когда ход является заблокированной атакой, и НИЧЕГО, когда это не атака. Таким образом, вы можете реализовать его в общем (public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }
), и при необходимости переопределять его на определенных ходах (слабые ходы, которые любой блок может блокировать, атаки, которые никогда не имеют обратной силы и т. Д.)Почему бы не использовать массив?
Я начну с самого начала. Я вижу шаблон, значения идут от 0 до 3, и вы хотите поймать все возможные значения. Вот Ваш столик:
когда мы смотрим на эту же двоичную таблицу, мы видим следующие результаты:
Может быть, вы уже видите какой-то шаблон, но когда я объединяю значения один и два, я вижу, что вы используете все значения 0000, 0001, 0010, ..... 1110 и 1111. Теперь давайте скомбинируем значение один и два, чтобы получить единое целое. 4-х битное целое число
Когда мы переводим это обратно в десятичные значения, мы видим очень возможный массив значений, где объединенные один и два могут быть использованы в качестве индекса:
Массив тогда
{0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}
, где его индекс просто один и два, объединены.Я не программист на Java, но вы можете избавиться от всех операторов if и просто записать их примерно так:
Я не знаю, если битовое смещение на 2 быстрее, чем умножение. Но это может стоить попробовать.
источник
Здесь используется немного битмага (вы уже делаете это, удерживая два бита информации (низкий / высокий и атака / блок) в одном целом числе):
Я не запускал его, только напечатал здесь, пожалуйста, перепроверьте.Идея, безусловно, работает. РЕДАКТИРОВАТЬ: теперь проверяется для каждого входа, работает нормально.Или я должен предложить разделить два бита информации на отдельные переменные? Код, основанный в основном на битовых операциях, подобных описанным выше, обычно очень сложно поддерживать.
источник
return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
Если честно, у каждого свой стиль кода. Я бы не подумал, что на производительность это повлияет слишком сильно. Если вы понимаете это лучше, чем использование версии переключателя, продолжайте использовать это.
Вы можете вкладывать if, поэтому потенциально для вашего последнего if-теста будет небольшое увеличение производительности, поскольку он не прошел бы столько же операторов if. Но в вашем контексте базового курса Java это, вероятно, не принесет пользы.
Итак, вместо ...
Вы бы сделали ...
И просто переформатируйте его так, как вы предпочитаете.
Это не делает код лучше, но, я полагаю, потенциально немного ускоряет его.
источник
Посмотрим, что мы знаем
1: ваши ответы симметричны для P1 (игрок один) и P2 (игрок два). Это имеет смысл для файтинга, но вы также можете использовать его для улучшения своей логики.
2: 3 удара 0 ударов 2 удара 1 ударов 3. Единственными случаями, не охваченными этими случаями, являются комбинации 0 против 1 и 2 против 3. Другими словами, уникальный стол победы выглядит следующим образом: 0 ударов 2, 1 удары 3, 2 удара 1, 3 удара 0.
3: если 0/1 идут друг против друга, то есть ничья без ударов, но если 2/3 идут друг против друга, то оба удара
Во-первых, давайте создадим одностороннюю функцию, сообщающую нам, если мы победили:
Затем мы можем использовать эту функцию для составления окончательного результата:
Хотя это, возможно, более сложно и, вероятно, медленнее, чем поиск в таблицах, предлагаемый во многих ответах, я считаю, что это превосходный метод, потому что он фактически инкапсулирует логику вашего кода и описывает его для всех, кто читает ваш код. Я думаю, что это делает его лучшую реализацию.
(Прошло много времени с тех пор, как я сделал какую-либо Java, поэтому извиняюсь, если синтаксис отключен, надеюсь, он все еще понятен, если я немного ошибся)
Кстати, 0-3 явно что- то значат ; это не произвольные значения, поэтому было бы полезно назвать их.
источник
Надеюсь, я правильно понимаю логику. Как насчет чего-то вроде:
Проверка одного высокого удара или одного низкого удара не блокируется и одинакова для второго игрока.
Изменить: Алгоритм не был полностью понят, "хит" присуждается при блокировке, который я не осознавал (Thx elias):
источник
У меня нет опыта работы с Java, поэтому могут быть некоторые опечатки. Пожалуйста, рассмотрите код как псевдокод.
Я бы пошел с простым переключателем. Для этого вам понадобится оценка одного номера. Однако для этого случая, поскольку
0 <= one < 4 <= 9
и0 <= two < 4 <= 9
, мы можем преобразовать оба целых числа в простое int, умноживone
на 10 и добавивtwo
. Затем используйте переключатель в полученном числе, как это:Есть еще один короткий метод, который я просто хочу обозначить как теоретический код. Однако я бы не стал его использовать, потому что он имеет дополнительную сложность, с которой вам обычно не хочется иметь дело. Дополнительная сложность исходит из базы 4 , потому что отсчет составляет 0, 1, 2, 3, 10, 11, 12, 13, 20, ...
На самом деле просто дополнительное примечание, на случай, если я что-то упустил из Java. В PHP я бы сделал:
источник
Так как вы предпочитаете вложенные
if
условия, вот другой способ.Обратите внимание, что он не использует
result
член и не меняет состояние.источник
else
цепи, но это не имело никакого значения.else if
утверждений, мы можем ускорить код с помощьюswitch
или просмотра таблиц.one==0
он выполнит код, то он должен будет проверить, еслиone==1
тогда, еслиone==2
и, наконец, еслиone==3
- Если бы там было еще, если, там, он не будет делать последние три проверки, потому что он выйдет из оператора после первого соответствия. И да, вы могли бы дополнительно оптимизировать использование оператора switch вместоif (one...
операторов, а затем дополнительно использовать другой переключатель в случаеone's
. Однако это не мой вопрос.Попробуйте это с корпусом переключателя. ..
Посмотрите здесь или здесь для получения дополнительной информации об этом
Вы можете добавить к нему несколько условий (не одновременно) и даже иметь опцию по умолчанию, если другие случаи не были выполнены.
PS: только если одно условие должно быть выполнено ..
Если 2 условия возникают одновременно .. Я не думаю, что можно использовать переключатель. Но вы можете уменьшить свой код здесь.
Оператор переключения Java несколько случаев
источник
Первое, что пришло мне в голову, - это по сути тот же ответ, который дал Франциско Презенсия, но несколько оптимизированный:
Вы могли бы дополнительно оптимизировать его, сделав последний случай (для 3) случаем по умолчанию:
Преимущество этого метода состоит в том, что легче увидеть, какие значения
one
иtwo
какие возвращаемые значения соответствуют, чем некоторые другие предлагаемые методы.источник
источник
; --unicorns
Вы можете использовать вместо переключателя случай переключения
if
Также стоит упомянуть, что, поскольку у вас есть две переменные, вы должны объединить две переменные, чтобы использовать их в switch
Проверить этот оператор Java для обработки двух переменных?
источник
Когда я рисую таблицу между одним / двумя и результатом, я вижу один шаблон,
Выше было бы сократить по крайней мере 3, если заявления. Я не вижу установленного шаблона и не могу многое почерпнуть из приведенного кода - но если такая логика может быть получена, это сократит количество операторов if.
Надеюсь это поможет.
источник
Хорошим моментом будет определение правил в виде текста, тогда вы сможете легче найти правильную формулу. Это извлечено из красивого представления массива laalto:
И здесь мы приведем некоторые общие комментарии, но вы должны описать их в терминах правил:
Вы, конечно, можете сократить это до меньшего количества кода, но обычно лучше понять, что вы кодируете, а не находить компактное решение.
Некоторое объяснение сложных хитов p1 / p2 было бы здорово, выглядит интересно!
источник
Самое короткое и все еще читаемое решение:
или даже короче
Не содержит никаких «магических» чисел;) Надеюсь, это поможет.
источник
static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }
источник
Мне лично нравится каскадные троичные операторы:
Но в вашем случае вы можете использовать:
Или вы можете заметить шаблон в битах:
Так что вы можете использовать магию:
источник
Вот довольно краткая версия, похожая на ответ JAB . Это использует карту для хранения, которая движется триумф над другими.
Пример:
Печать:
источник
static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();
вместо этого, это должно быть немного более эффективным.Я бы использовал карту, HashMap или TreeMap
Особенно если параметры не в форме
0 <= X < N
Как набор случайных натуральных чисел ..
Код
источник
Спасибо @Joe Harper за то, что я использовал вариант его ответа. Чтобы уменьшить его еще больше, так как 2 результата на 4 были одинаковыми, я уменьшил его еще больше.
Я могу вернуться к этому в какой-то момент, но если не будет большого сопротивления, вызванного множественными
if
заявлениями, я сохраню это сейчас. Я буду смотреть на матрицу таблицы и переключать решения оператора дальше.источник
Вот пример того, как это могло бы выглядеть, но использование целочисленных значений здесь по-прежнему некрасиво:
Было бы лучше использовать структурированный тип для ввода и вывода. На самом деле вход имеет два поля: положение и тип (блок или атака). Выход также имеет два поля: player1Wins и player2Wins. Кодирование этого в одно целое число усложняет чтение кода.
К сожалению, Java не очень хорошо выражает такие типы данных.
источник
Вместо этого сделайте что-то подобное
источник