Skip to content

Instantly share code, notes, and snippets.

@notcome
Created April 22, 2015 16:43
Show Gist options
  • Save notcome/66322c99fdc91e5b72a8 to your computer and use it in GitHub Desktop.
Save notcome/66322c99fdc91e5b72a8 to your computer and use it in GitHub Desktop.
陆陆的姓名生成器
import Control.Monad.State
import System.Random
type Range = (Int, Int) -> Int
rangify :: Int -> Range
rangify x (lo, hi) = x `mod` (hi - lo + 1) + lo
getRandomsR :: RandomGen g => State g [Range]
getRandomsR = map rangify . randoms <$> state split
dropAt :: Int -> [a] -> ([a], a)
dropAt n xs = let (heads, (x:tails)) = splitAt n xs
in (heads ++ tails, x)
replay :: (a -> b -> (b, c)) -> [a] -> b -> [c]
replay f [] _ = []
replay f (a:as) b = let (b', c) = f a b
in (:) c $ replay f as b'
picks :: [Int] -> [a] -> [a]
picks = replay dropAt
randomPick :: [a] -> Range -> a
randomPick xs range = let
len = length xs
index = range (0, len - 1)
in snd $ dropAt index xs
randomPicks :: Int -> [a] -> [Range] -> [a]
randomPicks n xs stream = let
len = length xs
ranges = [(0, len - x) | x <- [1..n]]
indexes = zipWith ($) stream ranges
in picks indexes xs
vowelSet = ["a", "o", "e", "u", "i", "y", "an", "on", "en", "un", "in", "yn"]
cnsntSet = ["dtθ", "bpfv", "szcj", "xγkg", "mnrl"]
shouldAddPrefix :: Int -> Int -> (Int, Bool)
shouldAddPrefix 6 _ = (5, False)
shouldAddPrefix x 4 = (x - 1, False)
shouldAddPrefix x _ = (x, True)
intercalate as bs = tail $ foldr1 (++) $ zipWith (\a b -> [a] ++ b) (' ':bs) as
buildName vs cs False = intercalate vs cs
buildName vs (p:cs) True = p:(intercalate vs cs)
randomName :: RandomGen g => State g String
randomName = do
numV <- state $ randomR (3, 6)
dice <- state $ randomR (1, 4)
let (numC, hasPrefix) = shouldAddPrefix numV dice
vs <- randomPicks numV vowelSet <$> getRandomsR
cs' <- randomPicks numC cnsntSet <$> getRandomsR
cs <- zipWith randomPick cs' <$> getRandomsR
return $ buildName vs cs hasPrefix
randomNames = sequence $ replicate 10 randomName
main = do
stdGen <- getStdGen
print $ evalState randomNames stdGen
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>姓名生成器</title>
<script>
// generate a random integer in [0, upperBound)
function random (upperBound) {
var x = Math.floor(Math.random() * upperBound);
return x;
}
// generate a random integer in [lowerBound, upperBound)
function randomInRange (lowerBound, upperBound) {
var seed = random(upperBound - lowerBound);
return seed + lowerBound
}
// identity function
function id (x) { return x; }
// pick up N elements from an array of elements
function pickUpFrom (num, set) {
var copy = set.map(id);
function work (len, num, array) {
if (num == 0)
return array.slice(len);
var index = random(len);
var tmp = array[len - 1];
array[len - 1] = array[index];
array[index] = tmp;
return work(len - 1, num - 1, array)
}
return work(copy.length, num, copy);
}
// pick up one element from an array of elements
function pickOne (set) {
var singleton = pickUpFrom(1, set);
return singleton[0];
}
var vowelSet = ['a', 'o', 'e', 'u', 'i', 'y', 'an', 'on', 'en', 'un', 'in', 'yn']
var consonantSet = [
['d', 't', 'θ'],
['b', 'p', 'f', 'v'],
['s', 'z', 'c', 'j'],
['x', 'γ', 'k', 'g'],
['m', 'n', 'r', 'l']];
function randomName () {
var numV = randomInRange(3, 7)
var dice = random(4)
if (numV == 6)
{ var numC = 5, hasPrefix = false; }
else if (dice == 3)
{ var numC = numV - 1, hasPrefix = false; }
else { var numC = numV, hasPrefix = true; }
var vowels = pickUpFrom(numV, vowelSet);
var cnsnts = pickUpFrom(numC, consonantSet).map(pickOne);
var result = '';
for (var i = 0; i < numV - 1; i ++)
result += vowels[i] + cnsnts[i];
result += vowels[numV - 1];
if (hasPrefix)
result = cnsnts[numC - 1] + result;
return result;
}
</script>
</head>
<body>
<button onclick="init()">点击刷新名字列表</button>
<ul id="container">
</ul>
<script>
function init () {
var ul = document.getElementById('container');
var html = '';
for (var i = 0; i < 10; i ++) {
var item = '<li>' + randomName() + '</li>';
html += item;
}
ul.innerHTML = html;
}
init();
</script>
</body>
</html>
// generate a random integer in [0, upperBound)
function random (upperBound) {
var x = Math.floor(Math.random() * upperBound);
return x;
}
// generate a random integer in [lowerBound, upperBound)
function randomInRange (lowerBound, upperBound) {
var seed = random(upperBound - lowerBound);
return seed + lowerBound
}
// identity function
function id (x) { return x; }
// pick up N elements from an array of elements
function pickUpFrom (num, set) {
var copy = set.map(id);
function work (len, num, array) {
if (num == 0)
return array.slice(len);
var index = random(len);
var tmp = array[len - 1];
array[len - 1] = array[index];
array[index] = tmp;
return work(len - 1, num - 1, array)
}
return work(copy.length, num, copy);
}
// pick up one element from an array of elements
function pickOne (set) {
var singleton = pickUpFrom(1, set);
return singleton[0];
}
var vowelSet = ['a', 'o', 'e', 'u', 'i', 'y', 'an', 'on', 'en', 'un', 'in', 'yn']
var consonantSet = [
['d', 't', 'θ'],
['b', 'p', 'f', 'v'],
['s', 'z', 'c', 'j'],
['x', 'γ', 'k', 'g'],
['m', 'n', 'r', 'l']];
function randomName () {
var numV = randomInRange(3, 7)
var dice = random(4)
if (numV == 6)
{ var numC = 5, hasPrefix = false; }
else if (dice == 3)
{ var numC = numV - 1, hasPrefix = false; }
else { var numC = numV, hasPrefix = true; }
var vowels = pickUpFrom(numV, vowelSet);
var cnsnts = pickUpFrom(numC, consonantSet).map(pickOne);
var result = '';
for (var i = 0; i < numV - 1; i ++)
result += vowels[i] + cnsnts[i];
result += vowels[numV - 1];
if (hasPrefix)
result = cnsnts[numC - 1] + result;
return result;
}
@notcome
Copy link
Author

notcome commented Apr 22, 2015

Pattern-match, especially at function decl level, is very convenient. (Hs over Js)
It's essential to have enough utility functions, like those in Prelude and Underscore.js. (Hs over Js)
Carefully controlled side-effects can significantly reduce LoC (Js over Hs), though it is hard to control side-effects.

Functional reactive programming is an interesting concept I should learn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment