通过递增获取字母数字字符串的最佳方法
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
这会按您想要的顺序生成输出。但是这个版本不能添加字符串。
我正在为 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
这会按您想要的顺序生成输出。但是这个版本不能添加字符串。