Добавить элемент Speci c в список, когда условие истинно - PullRequest
1 голос
/ 09 февраля 2020

Я хочу добавить кортеж, если бит установлен в 8-битном длинном числе (например, 146).

Мой код выглядит следующим образом, и Haskell просто печатает в первом истинном выражении:

returnPossibleMoves stone = if testBit (look stone) 0 then [(0,-1)] else [(0,0)] ++
                            if testBit (look stone) 1 then [(1,-1)] else [(0,0)] ++
                            if testBit (look stone) 2 then [(1,0)] else [(0,0)] ++
                            if testBit (look stone) 3 then [(1,1)] else [(0,0)] ++
                            if testBit (look stone) 4 then [(0,1)] else [(0,0)] ++
                            if testBit (look stone) 5 then [(-1,1)] else [(0,0)] ++
                            if testBit (look stone) 6 then [(-1,0)] else [(0,0)] ++
                            if testBit (look stone) 7 then [(-1,-1)] else [(0,0)]

с видом камня = 146 -> 10010010

Так что мое возвращение просто: [(0,0),(1,-1)]

Также возможно ли избавиться от остального?

Ответы [ 2 ]

3 голосов
/ 09 февраля 2020

Код можно исправить, добавив скобки здесь. Обратите внимание, что в случае else должен получиться пустой список. Например:

returnPossibleMoves stone =
    <b>(</b>if testBit (look stone) 0 then [(0,-1)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 1 then [(1,-1)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 2 then [(1,0)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 3 then [(1,1)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 4 then [(0,1)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 5 then [(-1,1)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 6 then [(-1,0)] else <b>[])</b> ++
    <b>(</b>if testBit (look stone) 7 then [(-1,-1)] else <b>[])</b>

При этом, выглядит не очень элегантно. Вы можете использовать zip здесь, чтобы составить 2 кортежа, где вы комбинируете ходы с битом для проверки, а затем filter свой список. Наконец, вы можете использовать map, чтобы распаковать 2 кортежа и сохранить первый элемент. Например:

returnPossibleMoves stone = map snd (filter (testBit (look stone) . fst) (zip [0..] moves))
    where moves = [(0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1), (-1,0), (-1,-1)]

или с пониманием списка, как @ chi предлагает :

returnPossibleMoves stone = [move | (i,move) <- zip [0..] moves, testBit (look stone) i]
    where moves = [(0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1), (-1,0), (-1,-1)]

Итак, вот результат:

Prelude Data.Bits> returnPossibleMoves 146
[(1,-1),(0,1),(-1,-1)]

(если мы установим look в id).

Это имеет смысл, поскольку для 146 установлены второй, пятый и восьмой биты, и поэтому мы возвращаем второй ((1,-1) ), пятый ((0,1)) и восьмой ((-1,-1)) элементы.

0 голосов
/ 10 февраля 2020

Просто для удовольствия, вот дорогой, но красивый способ:

intSin :: Int -> Int
intSin n = round (sin (pi*fromIntegral n/4))

returnPossibleMoves stone =
    [ (intSin i, intSin (i-2))
    | i <- [0..7]
    , testBit (look stone) i
    ]

Почему дорого? Потому что sin и умножение + деление с плавающей точкой почти наверняка дороже, чем простой подход с использованием таблицы поиска, как описано в других ответах. Но этот способ, я думаю, гораздо более описателен для значения функции ... по крайней мере для того, кто знает геометрическую интерпретацию sin.

Конечно, вы может также сделать intSin весьма эффективным, и сохранить пояснительное имя и пояснительную реализацию returnPossibleMoves; например:

intSin :: Int -> Int
intSin x
    | x .&. 3 == 0 = 0
    | otherwise = 1 - shiftR (x .&. 4) 1
...