KataWordWrap
Como parte de mi pomodoro y recomendación de mi estimado amigo Alfred (@alfredochv) me di un poco de tiempo para hacer la kata WordWrap. Les comparto mi solución, se me ocurrió otra manera de hacerlo, pero por falta de tiempo no la he concretado, tal vez la haga otro día.
Ejemplo con 4 columnas
You write a class called Wrapper
....|
You
writ
e a
clas
s
call
ed
Wrap
per
*/
class Wrapper{
static wrap = { inputStr, colNum ->
if(colNum <= 0 || inputStr.length() <= colNum){
return inputStr
}
def outputString = ""
def tempStr = inputStr[0..colNum-1]
def inputStrLength = inputStr.length()-1
def finPosTmp = 0
def iniPosInputStr = 0
def lastWhiteSpace = tempStr.lastIndexOf(' ')
if(tempStr.endsWith(' ')){
finPosTmp = tempStr.length() -1
iniPosInputStr = colNum-1
}else if(lastWhiteSpace > 0){
finPosTmp = lastWhiteSpace
iniPosInputStr = lastWhiteSpace + 1
}else{
finPosTmp = colNum-1
iniPosInputStr = colNum
}
outputString = tempStr[0..finPosTmp].trim()+'\n'
inputStr = inputStr[iniPosInputStr..inputStrLength].replaceAll(/^\s+/, '')
outputString + this.wrap(inputStr, colNum)
}
}
assert Wrapper.wrap('', 0) == ''
assert Wrapper.wrap('a', 0) == 'a'
assert Wrapper.wrap('a', 1) == 'a'
assert Wrapper.wrap('ab', 2) == 'ab'
assert Wrapper.wrap('ab', 1) == 'a\nb'
assert Wrapper.wrap('abc', 1) == 'a\nb\nc'
assert Wrapper.wrap('a bc def ghij klmnñ', 3) == 'a\nbc\ndef\nghi\nj\nklm\nnñ'
assert Wrapper.wrap('You write a class called Wrapper', 4) == 'You\nwrit\ne a\nclas\ns\ncall\ned\nWrap\nper'
assert Wrapper.wrap('You write a class called Wrapper', 5) == 'You\nwrite\na\nclass\ncalle\nd\nWrapp\ner'
assert Wrapper.wrap('You write a class called Wrapper', 9) == 'You\nwrite a\nclass\ncalled\nWrapper'
assert Wrapper.wrap('You write a class called Wrapper', 14) == 'You write a\nclass called\nWrapper'
assert Wrapper.wrap("wordword", 3) == "wor\ndwo\nrd"
assert Wrapper.wrap("word word", 3) == "wor\nd\nwor\nd"
assert Wrapper.wrap("word word", 4) == "word\nword"
assert Wrapper.wrap("word word", 5) == "word\nword"
assert Wrapper.wrap("word word", 6) == "word\nword"
assert Wrapper.wrap("word word word", 6) == "word\nword\nword"
assert Wrapper.wrap("word word word", 11) == "word word\nword"
- rodrigo salado anaya's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
Como que hay muchas variables
Como que hay muchas variables en la rutina y el nombre, aunque es largo, no me dice mucho respecto a porqué son necesarias, por ejemplo:
* colNum se refiere al ancho máximo de línea permitido en la cadena de salida, right?
* inputStrLength en realidad no es la longitud, sino el índice del último caracter de la cadena, ¿correcto?
Si el primer segmento de inputStr[0..colNum-1] termina en un espacio, entonces necesariamente lastWhitespace > 0, por lo que ambas condiciones se pueden unificar. Si tu preocupación es *no* dejar espacios en blanco finales en la cadena resultante, creo que el trim que usas cuando armas la cadena del resultado se debería hacer cargo de ellos, ¿no lo crees?
Recuerda que después de lograr que pase cada prueba, el paso siguiente es refactorizar :)
Es una buena solución, pero creo que es posible simplificarla aún más.
Saludos
Gracias por los comentarios...
Hola gracias por los comentarios.
Estoy de acuerdo, hay muchas variables, en esta etapa de refactoring trate de enfocarme en la legibilidad, sin buscar eliminar variables, aunque al final si quedaron cosas que digo “aí nanita eso no esta bien!!”, a ver que opinan.
Y gracias, fue una observación muy acertada la de simplificar las condiciones que mencionas (
inputStr[0..colNum-1] Y astWhitespace > 0
)Lo que no entendí fue la parte de inputStrLength en realidad no es la longitud, sino el índice del último caracter de la cadena, lo dices por el '-1' supongo, por que el método length() es el que trae la longitud, si es así estoy de acuerdo, solo que ese tipo de detalles se me van mucho.
Pues dejo el resultado de la 1ra fase de [re]factorización.
static inputStr
static lineWidth
static wrap = { inputStr, lineWidth ->
this.inputStr = inputStr
this.lineWidth = lineWidth
if(lineWidth <= 0 || inputStr.length() - 1 < lineWidth){
return inputStr
}
def indexOflastSpace = inputStr[0..lineWidth - 1].lastIndexOf(' ')
indexOflastSpace > 0 ?
this.getOutPutString(this.getTheCurrentLine(indexOflastSpace), this.getTheRestOfText(indexOflastSpace + 1))
:
this.getOutPutString(this.getTheCurrentLine(lineWidth - 1), this.getTheRestOfText(lineWidth))
}
static getOutPutString = {currentLine, theRestOfText->
currentLine + theRestOfText
}
static getTheCurrentLine = { endOfLine ->
inputStr[0..lineWidth - 1][0..endOfLine].trim()+ '\n'
}
static getTheRestOfText = { startOfNewLine ->
this.wrap(inputStr[startOfNewLine..inputStr.length() - 1].replaceAll(/^\s+/, ''), lineWidth)
}
}
Agradezco mucho tus comentarios y los de los demás
De hecho la prueba "You write
De hecho la prueba "You write a class called Wrapper" con ancho 9 es incorrecta; la tienes como "You\nwrite a\nclass\ncalled\nWrapper", lo cual es incorrecto:
Como puedes ver, el bloque "You write" mide exactamente 9 caracteres, seguidos por un espacio. Por lo tanto, la cadena debería dividirse inicialmente en "You write" y "a class..."
Saludos
Re: De hecho la prueba "You write
:O, es verdad, corrigiendo ese bug.
Gracias de nuevo!!!
Bueno, pues mi versión en
Bueno, pues mi versión en Java es más o menos así:
Ahora me vas a hacer instalar y aprender groovy nada más para contestarte en el mismo idioma... te pasas
:P
Un muy buen aporte que hace tu versión es la comprobación del ancho pedido: yo tenía una versión anterior que entraba en un ciclo infinito cuando width = 0!
Re: Bueno, pues mi versión en
Vaya, que bien y muy elegante solución. Jejeje creo que exagere un poco con mi solución :P, tu te la hubieras echado en JRuby, vale, muchas gracias por compartir tus conocimientos conmigo y la comunidad en gral.
Nos vemos pronto Alfred, un abrazo mi estimado.
Nos la echamos! :)
Nos la echamos! :)
Despues de mucho postergar y
Despues de mucho postergar y con mucho esfuerzo hice mi version solo para darme cuenta de que me faltaba una parte.
Ahora estoy viendo sus versiones y me surge la siguiente duda.
Para
El resultado deberia de ser:
555
6666
66
En vez de:
5555
5
6666
66
No? Porque si 55555 nunca va a caber en un ancho de columna de 4 en vez de dejar al 1 solito se le puede poner parte de la siguiente palabra.
no
el wordwrap estándar funciona como tu segundo ejemplo Oscar. El 1 se queda solo, y el 55555 se parte en dos porque no cabe en una línea.
Y porque no como en el
Y porque no como en el primero? Digo, de todas formas esta partido en 2 solo que se ocupan los 2 espacios vacios de la primera linea.
De cualquier forma, usando word (seeeee... le puse tamanio de letra 270px para probarlo) hace precisamente lo que dices; deja al 1 solito y parte al "55555" en ("5555" , "5") y no en ("55", "555")
No encuentro otra razon para que sea asi m'as que "por que si"
Je!
meh, de cualquier forma mi implementacion fallaba con algo mas basico aun
ps. si, me faltan los acentos (tildes) :(
Re: Y porque no como en el
Que onda, pues como dice ezamudio es la forma 2, hechale un ojo a las pruebas en ruby http://hg.code-cop.org/ruby-katas/src/tip/kata/word_wrap/20110713/test_w..., para mayor información jejeje
Saludos Oscar
Diferentes algoritmos = diferentes soluciones
En realidad Oscar tiene razón. En la definición del problema del kata no especifica cual de varias posibles formas de wordwrap usar, aunque también --como mencionó Rodrigo-- en las pruebas está implícito que lo que se trata de implementar es la variante "greedy", la cual trata de minimizar el número de líneas usadas colocando tantas palabras como quepan por linea. Es la variante más conocida y también la más usada por los editores de texto y procesadores de palabras.
Existen, sin embargo otras variantes, notablemente la usada por Tex, la cual se comporta de forma más parecida a lo descrito por Oscar, tratando de obtener una apariencia más pareja del margen derecho del texto, aunque esto tiene por consecuencia una mayor división de palabras. De hecho, si alguien aún lo recuerda, para quienes aprendimos a escribir a máquina con una máquina de escribir de verdad, este es el algoritmo que aprendimos de forma sub-conciente: *TING* tak. tak. tak. -guion- -vuelta de carro-...
Creo que es una variación interesante del Kata, pero habría que definir correctamente los requisitos de la misma para poderla considerar un Kata hecho y derecho, no?
Saludos
Si, la spec no es taaaaan
Si, la spec no es taaaaan clara en ese sentido.
Yo de plano ya tomé Word como spec :P