Skip to content

Instantly share code, notes, and snippets.

@dela3499
Last active August 29, 2015 13:57
Show Gist options
  • Save dela3499/9382995 to your computer and use it in GitHub Desktop.
Save dela3499/9382995 to your computer and use it in GitHub Desktop.
Auto-generating interesting names (a la Figure)
(require '[clojure.string :as string])
(defn insert-at [n coll elem]
"Insert elem as nth element of coll"
(let [[a b] (split-at n coll)]
(concat a [elem] b)))
(defn sample [coll n]
"Get n random samples from coll"
(repeatedly n #(rand-nth coll)))
(defn capitalize-words [s]
"Capitalize every word in a string"
(->> (string/split s #"\b")
(map string/capitalize)
(string/join)))
(defn get-random-name
"Get a random name with following constraints:
- has 6, 8, or 10 characters total
- name starts with consonant
- characters alternate between consonants and vowels
- name is either one or two capitalized words
- if two words, the first word is 2 or four characters long"
([] "Return a single name"
(let [n (rand-nth [3 4 5])
consonants "bcdfghjklmnpqrstv"
vowels "aeiouy"]
(->> (interleave (sample consonants n)
(sample vowels n))
((rand-nth [identity
#(insert-at (rand-nth [2 4]) % " ")]))
(apply str)
(capitalize-words))))
([n] "Return n names"
(repeatedly n get-random-name)))
(doseq [name (get-random-name 10)]
(println name))
import Graphics.Element exposing (show)
import Random exposing (..)
import Time exposing (second,every)
import Signal exposing (map,(<~))
import String exposing (toList)
import Array exposing (length,get,fromList,Array)
import List exposing (map,take,drop)
import Char
main = show <~ Signal.map (\x -> generateConfig (initialSeed (floor x)) |> getName) (every second)
type Length =
Everything | Len Int
type alias Config =
{vIndices: List Int,
cIndices: List Int,
length: Length}
generateConfig: Seed -> Config
generateConfig seed1 =
let (nChars, seed2) = generate (int 3 5) seed1
(vIndices, seed3) = generate (list nChars (int 0 1000)) seed2
(cIndices, seed4) = generate (list nChars (int 0 1000)) seed3
(length, seed5) = generate (int 0 2) seed4
config = {vIndices = vIndices,
cIndices = cIndices}
in
if length == 0
then {config | length = Everything}
else {config | length = Len (2 * length)}
getName: Config -> String
getName config =
let getLetters xs is = List.map (\i -> modGetChar i xs) is
vowels = getLetters "aeiouy" config.vIndices
consonants = getLetters "bcdfghjklmnpqrstv" config.cIndices
letters = interleave consonants vowels
in
case config.length of
Len x ->
letters
|> splitAt x
|> List.map String.fromList
|> List.map capitalize
|> String.join " "
Everything ->
letters
|> String.fromList
|> capitalize
modGet: Int -> Array a -> Maybe a
modGet i xs =
let j = i % (Array.length xs)
in Array.get j xs
modGetChar: Int -> String -> Char
modGetChar i s =
let x = modGet i (s |> String.toList |> Array.fromList)
in case x of
Just chara -> chara
Nothing -> 'a'
capitalize: String -> String
capitalize s =
let parts = String.uncons s
in case parts of
Just (h,t) -> String.cons (Char.toUpper h) t
Nothing -> ""
zip: List a -> List b -> List (a,b)
zip a b =
List.map2 (,) a b
interleave: List a -> List a -> List a
interleave a b =
zip a b
|> List.map (\(a,b) -> [a,b])
|> List.concat
splitAt: Int -> List a -> List (List a)
splitAt i xs =
[take i xs, drop i xs]
;;;
;;; The Figure music-creation mobile app (http://www.propellerheads.se/products/figure/)
;;; has a file auto-naming feature I thought I'd reproduce in different languages.
;;;
;;; After completing the Lisp Koans (https://github.com/google/lisp-koans), I thought I'd
;;; give Common Lisp a try. I compared the length of Common Lisp, Python, and Ruby versions and found
;;; Lisp to produce the most concise code (if general utility functions aren't included in program length).
;;;
;;; From what I could tell, all the random names generated by Figure followed this specification:
;;; - 6, 8, or 10 letters.
;;; - alternating consonants and vowels (any vowel allowed, and all consonants but w,x,y or z)
;;; - one or two words (each with first character capitalized)
;;; - (optionally, if making two words) a space after the first or second pair of letters
;;; Note: The last function shown reproduces a bug that hasn't yet been fixed.
;;; the macro "defun-with-optional-repeat" can only currently be used to define functions that take zero arguments
(setf *random-state* (make-random-state t)) ;req'd for random number creation
(defun stringify (x)
(coerce x 'string))
(defun listify (x)
(coerce x 'list))
(defun get-random-element (x)
"Returns a random element of a sequence."
(if (and x (typep x 'sequence))
(elt x (random (length x)))
(error "Argument to get-random-element empty or not a sequence")))
(defmacro defun-with-recursion (fname args &key start exec-when-atom)
(let ((arg (car args))) `(defun ,fname ,args
(progn ,start)
(cond ((null ,arg) nil)
((atom ,arg) (,exec-when-atom ,arg))
(t (mapcan #' ,fname ,arg))))))
(defun-with-recursion nice-print (x)
:start (fresh-line)
:exec-when-atom princ)
(defun-with-recursion flatten (x)
:exec-when-atom list)
(defun insert-nth (x n list)
"insert x into nth place in list"
(if (and n (> n 0))
(append (subseq list 0 n) (list x) (subseq list n))
list));no insertion in this case (n < 0 or is nil)
(defun exec-n-times (n f &rest other)
"evaluate function (f) for (n) times and store output in list"
(mapcar #'(lambda (x) (apply f other)) (make-list n)))
(defmacro defun-with-optional-repeat (fname fargs body)
`(defun ,fname ,(append fargs '(&optional n))
(if n
(exec-n-times n #' ,fname) ;; this will fail for functions with required arguments (since no arguments are passed to exec-n-times)
,body)))
(defun-with-optional-repeat create-name ()
(string-capitalize (stringify
(insert-nth #\space (get-random-element '(nil 2 4)) (flatten
(mapcar (lambda (x)
(mapcar #'get-random-element '("bcdfghjklmnpqrstv" "aeiouy")))
(make-list (get-random-element '(3 4 5)))))))))
(nice-print (create-name 10))
(defun-with-optional-repeat show-macro-failure (a)
a) ; this macro fails as specified in its definition above
; (nice-print (show-macro-failure 1 2))
"""This is a python version of the same random name generator"""
from string import ascii_lowercase as az
from random import choice
from itertools import *
def flatten(listOfLists):
"Flatten one level of nesting"
return list(chain.from_iterable(listOfLists))
def get_letters(n):
vowels = list('aeiouy')
consonants = list(set(list(az)) - set(list('wxyz')) - set(vowels))
return_vowels = [choice(vowels) for i in range(n/2)]
return_consonants = [choice(consonants) for i in range(n/2)]
return flatten(zip(return_consonants,return_vowels))
def get_name():
my_word = ''.join(get_letters(choice([6,8,10])))
l = choice([2,4])
my_words = ''.join([my_word[0:l],' ', my_word[l:]])
my_name = choice([my_word,my_words])
return my_name.title()
names = [get_name() for i in range(10)]
for name in names : print name
# Another random name generator - but this time in Ruby
def get_letters(n = 6)
vowels = 'aeiouy'.split('')
consonants = ('a'..'z').to_a - 'wxyz'.split('') - vowels
return_vowels = Array.new(n/2).map{vowels.sample}
return_consonants = Array.new(n/2).map{consonants.sample}
return return_consonants.zip(return_vowels).flatten
end
def get_name
letters = get_letters([6,8,10].sample).join
name = [letters, letters.insert([2,4].sample,' ')].sample
return name.split.map{|name| name.capitalize}.join(' ')
end
n = 10 ; puts Array.new(n).map{get_name}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment