Nombre / Generador de palabras usando DTMC en Ruby -- ruby campo con markov-chain camp codereview Relacionados El problema

Name/word generator using DTMC in Ruby


15
vote

problema

Español

Acabo de escribir un dtmc algoritmo enfocado en generar nombres, aunque podría usarse para generar muchas otras cosas . Está diseñado para ejecutarse desde la línea de comandos, por lo que se extrae la entrada de un archivo, con un par de opciones especificadas en la línea de comandos.

Estoy buscando específicamente estas cosas, aunque cualquier otro consejo también es bienvenido:

  • maneras de hacerlo más rápido o más eficiente en la memoria
  • maneras de hacerlo más idiomático para Ruby

Estoy perfectamente bien con el cambio del formato de entrada, aunque preferiría si se mantuviera igual. También estoy bien con hacer que el código sea un poco más ilegible si lo ayuda a hacerlo más eficiente o más corto, siempre que aún sea algo comprensible y fácil de mantener.

Este es mi código completo (versión con -h Ayuda disponible aquí ):

  #Constants! LINE_PART_DELIMITER = '|' LINK_DELIMITER = ';' LINK_HALF_MARK = ',' DEFAULT_NAME_COUNT = 10  # End with the standard error message format -- makes it easy to stay consistent def error(code, message)   puts "Error #{code}: #{message}"   puts 'Run this script with -e to see a list of error codes.'   abort "name_gen.rb: Error #{code}: #{message}" end  # Keys are candidates, values are probabilities def weighted_random_choice(picking_from)   current = 0   max = picking_from.values.inject :+   r_val = rand max   picking_from.each { |candidate, probability|     current += probability     return candidate if r_val < current   }   raise "r_val>max? #{r_val>max}. Error while picking a weighted random value from #{picking_from}" end  #Parsing the commandline arguments and suchlike syllable_separator = (/-d./ === ARGV[-1]) ? ARGV.pop[2..-1] : '' name_count = (/d+/ === ARGV[-1]) ? ARGV.pop.to_i : DEFAULT_NAME_COUNT file = ARGV.join ' '  #Parsing the file syllables = Hash.new false start = Hash.new 0 begin   IO.foreach(file) { |line|     name, start_prob, end_prob, links = line.split LINE_PART_DELIMITER     error 2, "`#{line}`" if links.nil? || end_prob.nil? || start_prob.nil? || name.nil?     links = links.split(LINK_DELIMITER).inject(Hash.new 0) { |memo, current_pair|       syl, prob = current_pair.split LINK_HALF_MARK       (prob = Integer prob) rescue error 3, "`#{name}`: `#{syl}`, `#{prob}`"       memo[syl.to_sym] += prob       memo     }     links[false] = end_prob.to_i     syllables[name.to_sym] = links     start[name.to_sym] += start_prob.to_i   } rescue Exception => message   puts message   error 1, "`#{file}`" end  #Validate that all syllables referenced actually exist! syllables.each { |syllable, links|   links.each { |link, _|     error(4, "`#{link}` in `#{syllable}`") if !!link && syllables[link.to_sym].nil?   } }  #Generating and printing the names name_count.times {   current_syllable = weighted_random_choice start   name_so_far = [current_syllable.to_s]   while (current_syllable = weighted_random_choice syllables[current_syllable.to_sym])     name_so_far << current_syllable.to_s   end   puts name_so_far.join syllable_separator }   

y este es un ejemplo de 'Diccionario' de ejemplo:

  a|1|1|b,2;c,2 b|0|3|a,0;c,2 c|0|0|a,1;b,1   

y aquí hay una muestra de diez nombres que puede generar (con el archivo de diccionario anterior):

  for i in {0..9}{a..z}{a..z}           {a..z}{0..9}{a..z}           {a..z}{a..z}{0..9}           {a..z}{a..z}{a..z}           {0..9}{0..9}{a..z}           {0..9}{a..z}{0..9}           {a..z}{0..9}{0..9}           {0..9}{0..9}{0..9}; do ... 0  
Original en ingles

I just wrote a basic DTMC algorithm focused on generating names, though it could be used to generate lots of other things. It's designed to be run from the command line, so input is taken from a file, with a couple of options specified on the command line.

I'm specifically looking for these things, though any other advice is also of course welcome:

  • Ways to make it faster or more memory-efficient
  • Ways to make it more idiomatic for Ruby

I'm perfectly fine with changing the input format, though I'd prefer if it stayed the same. I'm also fine with making the code a bit more unreadable if it helps make it more efficient or shorter, as long as it's still somewhat understandable and easy to maintain.

This is my full code (version with -h help available here):

#Constants! LINE_PART_DELIMITER = '|' LINK_DELIMITER = ';' LINK_HALF_MARK = ',' DEFAULT_NAME_COUNT = 10  # End with the standard error message format -- makes it easy to stay consistent def error(code, message)   puts "Error #{code}: #{message}"   puts 'Run this script with -e to see a list of error codes.'   abort "name_gen.rb: Error #{code}: #{message}" end  # Keys are candidates, values are probabilities def weighted_random_choice(picking_from)   current = 0   max = picking_from.values.inject :+   r_val = rand max   picking_from.each { |candidate, probability|     current += probability     return candidate if r_val < current   }   raise "r_val>max? #{r_val>max}. Error while picking a weighted random value from #{picking_from}" end  #Parsing the commandline arguments and suchlike syllable_separator = (/-d./ === ARGV[-1]) ? ARGV.pop[2..-1] : '' name_count = (/\d+/ === ARGV[-1]) ? ARGV.pop.to_i : DEFAULT_NAME_COUNT file = ARGV.join ' '  #Parsing the file syllables = Hash.new false start = Hash.new 0 begin   IO.foreach(file) { |line|     name, start_prob, end_prob, links = line.split LINE_PART_DELIMITER     error 2, "`#{line}`" if links.nil? || end_prob.nil? || start_prob.nil? || name.nil?     links = links.split(LINK_DELIMITER).inject(Hash.new 0) { |memo, current_pair|       syl, prob = current_pair.split LINK_HALF_MARK       (prob = Integer prob) rescue error 3, "`#{name}`: `#{syl}`, `#{prob}`"       memo[syl.to_sym] += prob       memo     }     links[false] = end_prob.to_i     syllables[name.to_sym] = links     start[name.to_sym] += start_prob.to_i   } rescue Exception => message   puts message   error 1, "`#{file}`" end  #Validate that all syllables referenced actually exist! syllables.each { |syllable, links|   links.each { |link, _|     error(4, "`#{link}` in `#{syllable}`") if !!link && syllables[link.to_sym].nil?   } }  #Generating and printing the names name_count.times {   current_syllable = weighted_random_choice start   name_so_far = [current_syllable.to_s]   while (current_syllable = weighted_random_choice syllables[current_syllable.to_sym])     name_so_far << current_syllable.to_s   end   puts name_so_far.join syllable_separator } 

And this is an example 'dictionary' file:

a|1|1|b,2;c,2 b|0|3|a,0;c,2 c|0|0|a,1;b,1 

And here's a sample of ten names it can generate (with the above dictionary file):

ab acb acabcacacb abca acacaca abcacacb acb a acbcacb a 
     

Lista de respuestas

7
 
vote
vote
La mejor respuesta
 

No me gusta cómo ha mezclado las preocupaciones de calcular con la producción. Lo sé ahora mismo Usted solo desea emitir un archivo, pero ¿qué sucede si decide que más tarde desea trabajar con estos datos en algún otro programa? Escribir al sistema de archivos es caro y lento. ¿Por qué escribir en un archivo y luego leerlo de nuevo en.

Modificaría esto para estar en dos partes. Uno para generar los nombres del diccionario y uno que usa esa clase para emitir un archivo. Esto deja que las cosas se abran a escribir la salida a IO estándar, algunas otras ui, o para otro programa para simplemente trabajar con los datos. La idea es que cada clase debe hacer una cosa y hacerlo bien.

En esta nota, cada uno de sus comentarios indica una oportunidad perdida para extraer un método bien nombrado que hace una y una sola cosa.

  char[]0  

Esto solo está mendigando ser un método llamado char[]1 . Debe devolver un objeto que representa esos tres valores de alguna manera sensata y bien nombrada.

Pido disculpas que he dejado la crítica sin ningún ejemplos de código. Normalmente proporcionaría algunos, pero ha pasado un tiempo desde que he escrito ningún rubí. Es mejor en este caso que te dejo para intentar limpiarte.

Intenta:

  • escribir métodos que hagan una y solo una cosa.
  • Crear abstracciones útiles por la forma de clases, incluso si son simplemente estructuras de datos simples para mantener la información.
  • Agregue un espacio en blanco vertical alrededor de la lógica que es demasiado trivial o demasiado entrelazada para ser su propio método.
 

I don't like how you've mixed the concerns of calculating with output. I know that right now you only want to output to a file, but what if you decide later that you want to work with this data in some other program? Writing to the file system is expensive and slow. Why write to a file and then read it back in.

I would modify this to be in two parts. One to generate the names from the dictionary and one that uses that class to output to a file. This leaves things open to writing the output to Standard IO, some other UI, or for another program to simply work with the data. The idea is that each class should do one thing and do it well.

On this note, each of your comments indicates a missed opportunity to extract a well named method that does one and only one thing.

#Parsing the commandline arguments and suchlike syllable_separator = (/-d./ === ARGV[-1]) ? ARGV.pop[2..-1] : '' name_count = (/\d+/ === ARGV[-1]) ? ARGV.pop.to_i : DEFAULT_NAME_COUNT file = ARGV.join ' ' 

This is just begging to be a method called parse_cmd_args. It should return some object that represents those three values in some sensible and well named way.

I apologize that I've left s critique without any code examples. Normally I would provide some, but it's been a while since I've written any Ruby. It's better in this case that I leave you to attempt a clean up yourself.

Try to:

  • Write methods that do one and only one thing.
  • Create useful abstractions by the way of classes, even if they're just simple data structures to hold information.
  • Add some vertical whitespace around logic that's too trivial or too intertwined to be its own method.
 
 
       
       

Relacionados problema

4  Escoger un estado aleatorio de un conjunto según las probabilidades  ( Picking a random state from a set based on probabilities ) 
Estoy escribiendo una implementación de cadena de Markov, y debe poder elegir un estado aleatorio de un conjunto de estados, donde cada estado tiene una proba...

4  Generador de texto Haskell Markov  ( Haskell markov text generator ) 
Soy nuevo en Haskell, y aquí está mi primer programa no totalmente trivial. Es el primer programa que tiendo a escribir en cualquier idioma: un generador de t...

16  Resuelva el estado de fase entre dos bloques de haplotipo utilizando las probabilidades de transición de Markov  ( Solve the phase state between two haplotype blocks using markov transition proba ) 
He pasado más de un año en Python, pero viene de fondo de biología. Debería decir que tengo algo de entendimiento de por-bucle y anidado para el bucle para en...

5  Generación de texto de cadena Markov en Python  ( Markov chain text generation in python ) 
Estoy dispuesto a escuchar cualquier consejo que tenga para mejorarlo. defmodule Board do alias Constants, as: C @moduledoc """ Board contains metho...

4  Implementación de una cadena de Markov SPARE  ( Implementation of a sparse markov chain ) 
Necesito crear una cadena de Markov Sparse. Se supone que debe recibir texto, por lo que la cantidad de filas o columnas puede ir fácilmente hasta 20000. Adem...

14  Generador de canciones de letra usando cadenas de Markov - Python  ( Song lyric generator using markov chains python ) 
He escrito un generador de canciones pop que utiliza la biblioteca de Markovify para producir letras según (solo para fines de prueba) canciones de Avril Lavi...

11  Implementación de una cadena de Markov  ( Implementation of a markov chain ) 
Leí sobre cómo las cadenas de Markov fueron útiles para crear generadores de texto y quería probarlo en Python. No estoy seguro de si esta es la forma corre...

6  Modelo Hidden Markov con Viterbi  ( Hidden markov model with viterbi ) 
Tengo una clase de modelo Hidden Markov con básicamente un solo método: obtener el mejor análisis de una secuencia de tokens de entrada basada en Viterbi. M...

7  Optimización de la implementación de cadena de Markov de orden variable  ( Optimizing variable order markov chain implementation ) 
He creado una cadena de orden de pedido variable construida en la parte superior de un árbol, pero no puedo entrenar en los conjuntos de datos y GT; 1MB de te...

36  Generador de nombres de país de Markov  ( Markov country name generator ) 
Escribí un generador de nombres de país en Python 3.5. Mi objetivo era obtener nombres al azar que parecieran los nombres del mundo real lo más posible. Cada ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos