我在我的公司代码库中看到了以下代码,并且自己想到了这个"该死的linq,我想将其转换为Haskell,看看它是什么比如实际的功能语言"
static Random random = new Random();
static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)])
.ToArray());
}
然而,由于在这种语言中生成随机数有多么尴尬,我在向Haskell进行简洁直接的翻译时遇到了一些麻烦。
我考虑了几种方法。 C#代码的最直接转换只生成一个随机索引,然后使用它代替random.Next(s.Length)
。但我需要生成多个索引,而不是单个索引。
然后我考虑列出IO Int
随机数操作列表,但我无法确定如何将IO
操作列表转换为实际随机数。
因此,与C#相比,我最终编写的Haskell代码看起来相当复杂(在这种情况下我并不期待),而且我甚至无法让它工作。
我的问题是,C#对Haskell的自然翻译是什么?或者更一般地说,你将如何在Haskell中生成一个指定长度的随机字符串(因为这种C#方式似乎不能很好地转换为Haskell)?
注意:我主要关注生成随机字符串的算法在Haskell中是什么样的。我对任何为我工作的标准库都不感兴趣
答案 0 :(得分:7)
对Haskell的自然转换涉及某种IO
(因为你需要随机性)。由于您实际上是在尝试执行选择字符n
次的操作,因此您希望使用replicateM
。然后,要获取范围内的随机数,您可以使用randomRIO
。
import Control.Monad (replicateM)
import System.Random (randomRIO)
randomString :: Int -> IO String
randomString n = replicateM n (do r <- randomRIO (0,m); pure (chars !! r))
where
chars = ['A'..'Z'] ++ ['0'..'9']
m = length chars
由于您希望在特定范围内只包含一串字符,因此这有点复杂。否则,你有一个班轮:randomString n = replicateM n randomIO
。
尽管如此,更忠实的翻译会使用conduit
。我不打算包含导入和语言编译(因为它们有点痛苦)。这看起来更像你在C#中所写的内容:
randomString' :: Int -> IO String
randomString' n = runConduit $ replicate n chars
.| mapM (\cs -> do r <- randomRIO (0,m); pure (cs !! r))
.| sinkList
where
chars = ['A'..'Z'] ++ ['0'..'9']
m = length chars