通过递增获取字母数字字符串的最佳方法

Best way to get alphanumeric string by incrementing

我正在为 F# 开发 Akka。我想按顺序生成字母数字字符串。

例如:第一个字符串是 0,第二个是 1,然后是 2,等等。当它达到 9 时,我希望它变成小写的“a”。当它到达“z”时,我希望它变成大写的“A”。当它到达“Z”时它应该变成00。依此类推...

我想按顺序搜索整个 space 个字符串,我不想随机生成它们。基本上我要求像 i++ 这样的增加函数,但我希望它具有 62 位算术而不是 10 位算术。 使用像 F# 这样的函数式语言,最好的方法是什么?我能否以某种方式利用字母的 ASCII 表示形式有序这一事实?但是这些数字伴随着 ASCII 中的字母表示。

奖金问题:我如何在这个系统上执行任何算术,比方说 x <- x+100

主要问题是您是想使用整数进行计算,然后在需要打印数字时将其转换为您的数字格式,还是想直接使用您的表示形式进行算术运算。在前一种情况下,你可以只对整数使用普通的数值运算,你只需要转换函数。在后一种情况下,您需要实现自己的数值运算。

对于 s++ 操作,基本的 F# 选项可以是递归函数。在这里,我将字符串表示为字符列表(以相反的顺序),例如,plusplus ['Z';'Z'] 变为 ['0';'0';'0']。操作本身只需要 alphabet 和一个后继字典:

let alphabet = ['0' .. '9'] @ ['a' .. 'z'] @ ['A' .. 'Z']
let succ = Seq.zip alphabet (Seq.tail alphabet) |> dict

let rec plusplus l = 
  match l with 
  | [] -> [ List.head alphabet ]
  | x::xs ->
      if succ.ContainsKey(x) then
        succ.[x] :: xs
      else 
        (List.head alphabet) :: plusplus xs

plusplus ['Z'; 'Z']

您基本上需要的只是一种将数字转换为字符串表示的方法,反之亦然。顺便说一下,对于每个基地来说,这个任务总是一样的。这是一个分步解决方案。

要将数字转换为二进制,可以使用以下算法。

let rec toBinary x =
    if x = 0 then "0"
    else
        let y = x % 2
        (toBinary ((x-y)/2)) + (string y)

例如,这就是您转换为八进制表示的方式。

let rec toOctal x =
    if x = 0 then "0"
    else
        let y = x % 8
        (toOctal ((x-y)/8)) + (string y)

如你所见,它总是一样的。在底座上制作一个模块,以获得最正确的数字。重复基数的减法和除法。

20

20 % 2 = 0  // 20 / 2
10 % 2 = 0  // 10 / 2
5  % 2 = 1  // (5-1) / 2
2  % 2 = 0  // 2 / 1
1  % 2 = 1  // (1-1) / 2
         0      

这样,您可以从从右到左生成数字,它适用于任何基数。

现在,您可以通过提供数字作为基数来使其更通用,而不是编写每个版本。

let rec toBase nbase x =
    if x = 0 then "0"
    else
        let y = x % nbase
        (toBase nbase ((x-y)/nbase)) + (string y)

为了使其更通用,您可以提供一个字符串列表和您的映射。像这样。

let rec toBase' nbase x =
    let bse = List.length nbase

    if x = 0 then 
        nbase.[0] 
    else
        let y = x % bse
        (toBase' nbase ((x-y)/bse)) + (nbase.[y])

现在,您可以创建任意映射。

let toBin  = toBase' ["0";"1"]
let toABCD = toBase' ["A";"B";"C";"D"]
let toHex  = toBase' ["0";"1";"2";"3";"4";"5";"6";"7";"8";"9";"A";"B";"C";"D";"E";"F"]
let toOct  = toBase' ["0";"1";"2";"3";"4";"5";"6";"7"]

接下来,你应该做相反的事情。映射一个字符串,返回一个数字。通常的方法是理解数字是某个位置的乘法。例如二进制数“1010”的实际意思是:

(1 * 8) + (0 * 4) + (1 * 2) + (0 * 1)

作为代码:

let rec toInt nbase x =
    let bse = List.length nbase
    let mut = pown bse (String.length x - 1)

    if x = "" then 
        0 
    else
        let fc    = string (x.[0])
        let index = List.findIndex (fun e -> e = fc) nbase
        (toInt nbase (x.[1..])) + (index * mut)

现在你可以定义反向了。

let toBin   = toBase' ["0";"1"]
let fromBin = toInt   ["0";"1"]

let toABCD   = toBase' ["A";"B";"C";"D"]
let fromABCD = toInt   ["A";"B";"C";"D"]

let toHex   = toBase' ["0";"1";"2";"3";"4";"5";"6";"7";"8";"9";"A";"B";"C";"D";"E";"F"]
let fromHex = toInt   ["0";"1";"2";"3";"4";"5";"6";"7";"8";"9";"A";"B";"C";"D";"E";"F"]

let toOct   = toBase' ["0";"1";"2";"3";"4";"5";"6";"7"]
let fromOct = toInt   ["0";"1";"2";"3";"4";"5";"6";"7"]

如果您现在想要一个数字流,您可以 List.map 您的数字,或从中创建一个序列。例如,二进制数流。这个序列就是你想要的“++”操作。一个递增的惰性序列,你所有的变体。

let binaryStream = Seq.initInfinite toBin

for x in Seq.take 10 binaryStream do
    printfn "%s" x

// Will print
// 0
// 01
// 010
// 011
// 0100
// 0101
// 0110
// 0111
// 01000
// 01001

将打印第一个,10 个二进制数。如果要将 +100 加到字符串,首先将其转换为数字,进行数字乘法,然后将其转换回字符串。或者提供你想要的那些功能。例如添加两个二进制字符串。

let addBin x y =
    toBin ((fromBin x) + (fromBin y))

addBin "1000" "1010"  // 8 + 10

现在你可以做你的base62了。例如,要打印前 200 个字符串。

let toB62 = 
    toBase' (
        List.map string [0..9]
        @ List.map string ['a' .. 'z']
        @ List.map string ['A' .. 'Z'])

let fromB62 =
    toInt (
        List.map string [0..9]
        @ List.map string ['a' .. 'z']
        @ List.map string ['A' .. 'Z'])

let b62stream = Seq.initInfinite toB62

for x in Seq.take 200 b62stream do
    printfn "%s" x

我再补充一个答案,感觉我之前的答案不对。使用其他代码,您可以将任何数字转换为任何其他基数,反之亦然,但它并不能完全满足您的需要。

在某些情况下,我认为您真正需要的是 Cartesian Product

在你的情况下,你想要生成字符串,在你的特殊情况下,你会 add/prepend 每个字符到一个字符串列表。然后根据字符串的长度重复此操作。

let rec product depth xs = seq {
    if depth = 0 then 
        yield ""
    else
        for x in xs do
            for str in product (depth-1) xs do
                yield x + str
}

示例:

product 1 ["0";"1"]
// seq ["0"; "1"]

product 2 ["0";"1"]
// seq ["00"; "01"; "10"; "11"]

product 3 ["0";"1"]
// seq ["000"; "001"; "010"; "011"
//      "100"; "101"; "110"; "111"

这会生成给定大小的所有可能字符串。但我猜你想 遍历所有组合,直到一个大小。你可以这样做,像这样。

let productUpto depth xs = seq {
    for x=1 to depth do
        yield! product x xs
}

现在代码如下

for x in productUpto 4 ["A";"B"] do
    printfn "%s" x

打印:

A
B
AA
AB
BA
BB
AAA
AAB
ABA
ABB
BAA
BAB
BBA
BBB
AAAA
AAAB
AABA
AABB
ABAA
ABAB
ABBA
ABBB
BAAA
BAAB
BABA
BABB
BBAA
BBAB
BBBA
BBBB

这会按您想要的顺序生成输出。但是这个版本不能添加字符串。