如何将机器int用于Haskell中设置的有限操作位?

如何将机器int用于Haskell中设置的有限操作位?,haskell,Haskell,我想用一个machine int来表示一个包含零个或多个数字1..9的集合。这是为了满足一种愚蠢的痴迷,即我可以用Haskell构建一个数独求解算法的速度有多快,但这一切都是为了扩展我的Haskell教育。我发现,除了我能找到的书本练习之外,我还有一个真正的问题需要解决。对于一个“非常难”但不需要猜测就可以解决的难题,我将它缩短到几毫秒,但我希望下一代的性能可以用微秒来衡量:) 我需要进行以下操作: 初始化包含所有成员的集合,对应于十进制数字511或0b111111111。我在想s1=MyBi

我想用一个machine int来表示一个包含零个或多个数字1..9的集合。这是为了满足一种愚蠢的痴迷,即我可以用Haskell构建一个数独求解算法的速度有多快,但这一切都是为了扩展我的Haskell教育。我发现,除了我能找到的书本练习之外,我还有一个真正的问题需要解决。对于一个“非常难”但不需要猜测就可以解决的难题,我将它缩短到几毫秒,但我希望下一代的性能可以用微秒来衡量:)

我需要进行以下操作:

  • 初始化包含所有成员的集合,对应于十进制数字511或0b111111111。我在想s1=MyBitSet(511)之类的东西,但是任何不需要单独设置每个位的方法都可以
  • 设置减法-类似于0b111111111-0b10101=>111101010。在按位运算中,我相信这将是s1或补码(s2)
  • 成员-像s2={2,4,6,7,8,9}
我一直在为我在搜索中找到的代码感到困惑,但部分原因是我的Haskell技能仍在发展中,部分原因是这是一个有点专业化的优化,我在取得进展方面遇到了困难


你能给我指出正确的方向吗?

根据上面@user2407038的建议,我制定了以下模块,似乎很好地完成了这项工作,并让我有机会第一次在脑海中直接了解哈斯克尔的一些概念:

--
-- A simple, hopefully very fast Sudoku markup set implementation
--

module MarkupSet where

import Data.Bits ((.&.), complement, setBit, testBit)
import Data.Word (Word)
import Test.HUnit


type MarkupSet = Word


-- initialized MarkupSet value, 511 decimal, 0b111111111 binary
defaultMarkupSet :: MarkupSet
defaultMarkupSet = 511

-- an empty set is also handy in some cases
emptyMarkupSet :: MarkupSet
emptyMarkupSet = 0

-- construct a MarkupSet from an Int (Word) value, e.g. markupSet 42
markupSet :: Word -> MarkupSet
markupSet x = x

-- construct a MarkupSet from list of values, e.g. [1,3,5]
fromValues :: [Int] -> MarkupSet
fromValues = foldl (\acc n -> setBit acc (n-1)) emptyMarkupSet
-- based on comment from @dfeuer, updated from:
-- fromValues [] = markupSet 0
-- fromValues (n:ns) = setBit (fromValues ns) (n - 1)

-- a list of the integer members 1..9 in a MarkupSet, e.g. [1,3,5]
enumMembers :: MarkupSet -> [Int]
enumMembers s = [ i+1 | i <- [0..8], testBit s i]

-- set difference, aka complement set: members of s not in s'
-- e.g difference {1,3,5,8} {1,2,3} -> {5,8}
difference :: MarkupSet -> MarkupSet -> MarkupSet
difference s s' = s .&. (complement s')

-- 9-digit binary string representation, e.g. "001011010"
showBits :: MarkupSet -> String
showBits s = [ if testBit s i then '1' else '0' | i <- [8,7..0] ]


-- ===================================================================
-- Tests
-- ===================================================================

case_default_has_all_members = TestCase $ assertEqual
    "defaultMarkupSet should have all bits set"
    "111111111"
    (showBits defaultMarkupSet)

case_construct_from_int = TestCase $ assertEqual
    "can construct from Int literal"
    "000101010"
    (showBits $ markupSet 42)

case_construct_from_values = TestCase $ assertEqual
    "can construct from list of ints in range [1..9]"
    297
    (fromValues [1,4,6,9])

case_enum_int_members = TestCase $ assertEqual
    "can enumerate its integer members"
    [2,4,6]
    (enumMembers $ markupSet 42)

case_subtract_sets = TestCase $ assertEqual
    "can calculate set difference"
    [1,3,5,7,8,9]
    (enumMembers $ difference defaultMarkupSet (markupSet 42))


tests = 
    [ case_default_has_all_members
    , case_construct_from_int
    , case_construct_from_values
    , case_enum_int_members
    , case_subtract_sets
    ]


main = do
    runTestTT $ TestList tests
--
--一个简单、希望非常快速的数独标记集实现
--
模块标记在哪里
导入数据位((.&.)、补码、setBit、testBit)
导入数据。Word(Word)
导入测试.HUnit
键入MarkupSet=Word
--初始化的MarkupSet值,511十进制,0b111111111二进制
defaultMarkupSet::MarkupSet
defaultMarkupSet=511
--空集在某些情况下也很方便
emptyMarkuper::markuper
EmptyMarkuper=0
--从Int(Word)值构造MarkupSet,例如MarkupSet 42
markupSet::Word->markupSet
标记x=x
--根据值列表构造一个标记,例如[1,3,5]
fromValues::[Int]->MarkupSet
fromValues=foldl(\acc n->setBit acc(n-1))清空标记
--根据@dfeuer的评论,更新自:
--fromValues[]=0
--fromValues(n:ns)=setBit(fromValues ns)(n-1)
--标记中整数成员1..9的列表,例如[1,3,5]
enumMembers::MarkupSet->[Int]

enumMembers=[i+1 | i早期优化是程序员的堕落。分析您的代码,找到热点,我敢打赌,您会发现并不是您的整数表示最让您慢。您知道“集”减法和“集”吗成员资格可以通过按位操作写入。嗯,这看起来很有趣@user2407038,我来看看那个模块,我想它可能会起作用,谢谢:)我被困在Data.BitSet上,错过了那一个:)@bheklillr是的,你当然是对的,通常我不会在没有证据的情况下追求这种事情这是一个根本原因。它是it’现在任何能引起我兴趣的方向都是好的,都会激发新的探索:)我会警惕自己养成坏习惯:)@scanny单纯地把它作为一种学习练习是完全不同的,在这种情况下,我鼓励你努力追求它!我只是警告你不要这样做,因为我正是被它咬了一口在过去一周的工作中,我认为使用C会比Python给我带来巨大的速度提升,但对于这个特别的问题,NumPy已经尽我所能让它变得最快了。
fromValues
是一个折页,你应该把它写成一个折页,以利用折页的特殊优化规则。但是当你当然,我想把它构造成一个左折叠。我不认为这对一个数独解算器来说有什么意义,但是如果你想学习基本的Haskell优化,试试看。谢谢@dfeuer,我会的!:)