2010-09-30 7 views
40

Estoy pasando por los koans de rubí, estoy en 151 y choqué contra una pared de ladrillos.Ruby Koan 151 elevando excepciones

Aquí es el koan:

# You need to write the triangle method in the file 'triangle.rb' 
require 'triangle.rb' 

class AboutTriangleProject2 < EdgeCase::Koan 
    # The first assignment did not talk about how to handle errors. 
    # Let's handle that part now. 
    def test_illegal_triangles_throw_exceptions 
    assert_raise(TriangleError) do triangle(0, 0, 0) end 
    assert_raise(TriangleError) do triangle(3, 4, -5) end 
    assert_raise(TriangleError) do triangle(1, 1, 3) end 
    assert_raise(TriangleError) do triangle(2, 4, 2) end 
end 
end 

Luego, en triangle.rb tenemos:

def triangle(a, b, c) 
    # WRITE THIS CODE 
    if a==b && a==c 
    return :equilateral 
    end 
    if (a==b && a!=c) || (a==c && a!=b) || (b==c && b!=a) 
    return :isosceles 
    end 
    if a!=b && a!=c && b!=c 
    return :scalene 
    end 
    if a==0 && b==0 && c==0 
    raise new.TriangleError 
    end 



end 

# Error class used in part 2. No need to change this code. 
class TriangleError < StandardError 

end 

Estoy más allá confundido - ninguna ayuda sería muy apreciada!

EDIT: Para completar este koan, tengo que poner algo en la clase TriangleError - pero no tengo ni idea de lo

ACTUALIZACIÓN: Esto es lo que la cosa Karma koan está diciendo:

<TriangleError> exception expected but none was thrown. 
+0

¿Cuál es la pregunta aquí? –

+0

Hola Daniel: actualicé mi pregunta para ser un poco más clara – Elliot

+4

Aquí está mi solución mínima: https://gist.github.com/1126423 – danneu

Respuesta

44
  1. Un triángulo no debe tener ningún lado de longitud 0. Si lo hace, es un segmento de línea o un punto, según cuántos lados son 0.
  2. La longitud negativa no tiene sentido.
  3. Cualquier dos lados de un triángulo debe sumar más que el tercer lado.
  4. Consulte 3, y concéntrese en "más".

No debería necesitar cambiar el código TriangleError, AFAICS. Parece que tu sintaxis es un poco rara. Intenta cambiar

raise new.TriangleError 

a

raise TriangleError, "why the exception happened" 

Además, usted debe estar probando los valores (y lanzar excepciones) antes de hacer nada con ellos. Mueva las cosas de excepción al comienzo de la función.

+0

Hola cHao, gracias por tu comentario. Entiendo cuándo plantear las excepciones, simplemente no entiendo lo que entra en la clase TriangleError – Elliot

+3

¿No dice el código "No hay necesidad de cambiar este código"? Simplemente debería funcionar. – cHao

+0

Eso era de la parte 1 de la clase, en la parte 2 (esta parte) cambias el código – Elliot

3

Definitivamente no actualizo la clase TriangleError - Estoy atascado en 152 yo mismo. Creo que necesito usar el teorema de pythag aquí.

def triangle(a, b, c) 
    # WRITE THIS CODE 

    if a == 0 || b == 0 || c == 0 
    raise TriangleError 
    end 

    # The sum of two sides should be less than the other side 
    if((a+b < c) || (a+c < b) || (b+c < a)) 
    raise TriangleError 
    end 
    if a==b && b==c 
    return :equilateral 
    end 
    if (a==b && a!=c) || (a==c && a!=b) || (b==c && b!=a) 
    return :isosceles 
    end 
    if(a!=b && a!=c && b!=c) 
    return :scalene 
    end 


end 

# Error class used in part 2. No need to change this code. 
class TriangleError < StandardError 
end 
+2

Lo que has hecho está bien, pero no es el teorema de Pitágoras;) El teorema de Pitágoras establece c^2 = a^2 + b^2 y solo es válido para triángulos en ángulo recto. –

+2

Si comienza haciendo 'a, b, c = [a, b, c] .sort', encontrará que el resto de su código es mucho más simple. ;) – davidchambers

1

Esto es lo que escribí y funcionó bien.

def triangle(a, b, c) 
    # WRITE THIS CODE 
    raise TriangleError, "Sides have to be greater than zero" if (a == 0) | (b == 0) | (c == 0) 
    raise TriangleError, "Sides have to be a postive number" if (a < 0) | (b < 0) | (c < 0) 
    raise TriangleError, "Two sides can never be less than the sum of one side" if ((a + b) < c) | ((a + c) < b) | ((b + c) < a) 
    raise TriangleError, "Two sides can never be equal one side" if ((a + b) == c) | ((a + c) == b) | ((b + c) == a) 
    return :equilateral if (a == b) & (a == c) & (b == c) 
    return :isosceles if (a == b) | (a == c) | (b == c) 
    return :scalene 

end 

# Error class used in part 2. No need to change this code. 
class TriangleError < StandardError 
end 
+0

Cualquier razón para hacer cuatro pruebas cuando puede hacer esto: 'raise TriangleError," Sides debe por números mayores que cero "if (a <= 0) || (b <= 0) || (c <= 0) raise TriangleError, "No se pueden agregar dos lados para ser menor que o igual al otro lado" si (a + b <= c) || (a + c <= b) || (b + c <= a) ' –

+2

¿Por qué usa una sola tubería (' | ')? –

9

Me gusta Cory's answer. Pero me pregunto si hay alguna razón o nada que ganar por tener cuatro pruebas, cuando podría tener dos:

raise TriangleError, "Sides must by numbers greater than zero" if (a <= 0) || (b <= 0) || (c <= 0) 
raise TriangleError, "No two sides can add to be less than or equal to the other side" if (a+b <= c) || (a+c <= b) || (b+c <= a) 
+0

Debería funcionar bien. La única diferencia útil es la capacidad de especificar diferentes mensajes de error para, por ejemplo, cero frente a longitudes negativas. Lo cual realmente no veo la necesidad de ninguno. La gente solo hace 4 comprobaciones porque hay 4 casos explícitos que la prueba está buscando y están codificando para pasar la prueba. – cHao

+0

Es cierto, aunque mayor que cero implica positivo, ¿no? –

+1

Sí. Pero hay 4 pruebas que el código tiene que pasar, y algunas personas hacen una visión de túnel donde se enfocan solo en la prueba actual, ignorando a todas las demás. Me imagino que alguien dirá que es más correcto o algo así desde una perspectiva TDD, pero sí. No hay ninguna razón para no condensarlo en 2 controles, IMO. – cHao

1

Usted tiene que comprobar que el nuevo triángulo creado no rompen el "Triángulo de la desigualdad". Puedes asegurarte esto con esta pequeña fórmula.

if !((a-b).abs < c && c < a + b) 
    raise TriangleError 
end 

Cuando se obtiene el error:

<TriangleError> exception expected but none was thrown. 

su código es probablemente una excepción durante la creación de un triángulo regular en este archivo. about_triangle_project.rb

+0

Debería asegurarse de que 'a',' b' y 'c' estén en un cierto orden, para que su prueba solo cubra 1/3 de los posibles casos. – cHao

1

Para el Koan about_triangle_project_2.rb no es necesario cambiar la clase TriangleError.Inserte este código antes de su algoritmo de triángulo para pasar todas las pruebas:

if ((a<=0 || b<=0 || c<=0)) 
    raise TriangleError 
end 

if ((a+b<=c) || (b+c<=a) || (a+c<=b)) 
    raise TriangleError 
end 
1

Esto le tomó un poco de tiempo del cerebro. Pero aquí está mi solución

def triangle(a, b, c) 
    # WRITE THIS CODE 
    raise TriangleError, "All sides must be positive number" if a <= 0 || b <= 0 || c <= 0 
    raise TriangleError, "Impossible triangle" if (a + b + c - (2 * [a,b,c].max) <= 0 ) 

    if(a == b && a == c) 
     :equilateral 
    elsif (a == b || b == c || a == c) 
     :isosceles 
    else 
    :scalene 
    end 
end 
2

terminé con este código:

def triangle(a, b, c) 
    raise TriangleError, "impossible triangle" if [a,b,c].min <= 0 
    x, y, z = [a,b,c].sort 
    raise TriangleError, "no two sides can be < than the third" if x + y <= z 

    if a == b && b == C# && a == C# XXX: last check implied by previous 2 
     :equilateral 
    elsif a == b || b == c || c == a 
     :isosceles 
    else 
     :scalene 
    end 
end 

no me gusta la segunda condición/subir, pero estoy seguro de cómo mejorar aún más.

3

Quería un método que analizara todos los argumentos de manera efectiva en lugar de confiar en el orden dado en las afirmaciones de prueba.

def triangle(a, b, c) 
    # WRITE THIS CODE 
    [a,b,c].permutation { |p| 
    if p[0] + p[1] <= p[2] 
     raise TriangleError, "Two sides of a triangle must be greater than the remaining side." 
    elsif p.count { |x| x <= 0} > 0 
     raise TriangleError, "A triangle cannot have sides of zero or less length." 
    end 
    } 

    if [a,b,c].uniq.count == 1 
    return :equilateral 
    elsif [a,b,c].uniq.count == 2 
    return :isosceles 
    elsif [a,b,c].uniq.count == 3 
    return :scalene 
    end 
end 

Esperemos que esto ayude a los demás a darse cuenta de que hay más de una manera de despellejar a un gato.

+0

Buen enfoque funcional para la clasificación. Podría usar una declaración de caso en lugar de cadena/elsif al final para evitar repetir llamadas a uniq y contar. –

+0

'return' no es necesario al final – Evmorov

7

No necesita modificar la excepción. Algo como esto debería funcionar;

def triangle(*args) 
    args.sort! 
    raise TriangleError if args[0] + args[1] <= args[2] || args[0] <= 0 
    [nil, :equilateral, :isosceles, :scalene][args.uniq.length] 
end 
2

También puede probar a la instancia con la excepción:

raise TriangleError.new("All sides must be greater than 0") if a * b * c <= 0 
1

Aquí está mi versión ... :-)

def triangle(a, b, c) 

    if a <= 0 || b <= 0 || c <= 0 
    raise TriangleError 
    end 

    if a + b <= c || a + c <= b || b + c <= a 
    raise TriangleError 
    end 

    return :equilateral if a == b && b == c 
    return :isosceles if a == b || a == c || b == c 
    return :scalene  if a != b && a != c && b != c 

end 
27

se le olvidó el caso en que a, b, o c son negativos:

def triangle(a, b, c) 
    raise TriangleError if [a,b,c].min <= 0 
    x, y, z = [a,b,c].sort 
    raise TriangleError if x + y <= z 
    [:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1) 
end 
+0

¡Buena corrección, gracias! – Sergey

+1

Más conciso si ordena primero y luego pruebe una vez con la condición 'x <= 0 || x + y <= z'. Además, no es necesario usar fetch como '[a, b, c] .uniq.size' siempre estará en' (1..3) 'debido al hecho de que' a, b, c' son parámetros necesarios. – SLD

+3

conciso. Pero no estoy seguro si esta brevedad compensa la legibilidad sacrificada (especialmente para las personas que leerán este código más adelante). –

1

Esto es lo que terminé p con. Es una especie de combinación de algunos de los ejemplos anteriores con mi propia visión única de la excepción de desigualdad triangular (también considera el caso degenerado). Parece funcionar.

def triangle(a, b, c) 

    raise TriangleError if [a,b,c].min <= 0 
    raise TriangleError if [a,b,c].sort.reverse.reduce(:-) >= 0 

    return :equilateral if a == b && b == c 
    return :isosceles if a == b || a == c || b == c 
    return :scalene 

end 
11

terminó haciendo esto:

def triangle(a, b, c) 
    a, b, c = [a, b, c].sort 
    raise TriangleError if a <= 0 || a + b <= c 
    [nil, :equilateral, :isosceles, :scalene][[a, b, c].uniq.size] 
end 

Gracias a comentaristas aquí :)

2

De hecho, en el código siguiente a la condición de un < = 0 es redundante. a + b siempre será menor que c si un < 0 y sabemos que b < c

raise TriangleError if a <= 0 || a + b <= c 
0

León gana en elegancia de lujo, Benji por su conocimiento de la API de matriz. Aquí está mi respuesta elegante bruta:

def triangle(a, b, c) 
    [a, b, c].each { | side | raise TriangleError, "Sides must be positive" unless side > 0 } 
    raise TriangleError, "Two sides can never be less than or equal to third side" if ((a + b) <= c) | ((a + c) <= b) | ((b + c) <= a) 

    return :equilateral if (a == b) && (b == c) 
    return :isosceles if (a == b) || (b == c) || (a == c) 
    return :scalene 
end 
0

No es necesario cambiar el código TriangleError para cualquier desafío. Solo necesita comprobar si hay triángulos no válidos y si el triángulo no lo es, generar el error.

def triangle(a, b, c) 
    if a==0 && b==0 && c==0 
    raise TriangleError, "This isn't a triangle" 
    end 
    if a <0 or b < 0 or c <0 
    raise TriangleError, "Negative length - thats not right" 
    end 
    if a + b <= c or a + c <= b or b + c <= a 
    raise TriangleError, "One length can't be more (or the same as) than the other two added together. If it was the same, the whole thing would be a line. If more, it wouldn't reach. " 
    end  
    # WRITE THIS CODE 
    if a == b and b == c 
    return :equilateral  
    end  
    if (a==b or b == c or a == c) 
    return :isosceles 
    end 
    :scalene  
end 
6
def triangle(a, b, c) 
    [a, b, c].permutation do |sides| 
    raise TriangleError unless sides[0] + sides[1] > sides[2] 
    end 
    case [a,b,c].uniq.size 
    when 3; :scalene 
    when 2; :isosceles 
    when 1; :equilateral 
    end 
end 
0

hay algunas personas absolutamente brillantes en StackOverflow ...Me acuerdo de que cada vez que visita: D Sólo para contribuir a la conversación, aquí está la solución que se me ocurrió:

def triangle(a, b, c) 
    raise TriangleError if [a,b,c].min <= 0 
    x,y,z = [a,b,c].sort 
    raise TriangleError if x + y <= z 

    equal_sides = 0 
    equal_sides +=1 if a == b 
    equal_sides +=1 if a == c 
    equal_sides +=1 if b == c 

    # Note that equal_sides will never be 2. If it hits 2 
    # of the conditions, it will have to hit all 3 by the law 
    # of associativity 
    return [:scalene, :isosceles, nil, :equilateral][equal_sides] 
end 
1

Aquí está mi respuesta elegante, con mucha ayuda de los comentarios anteriores

def triangle(a, b, c) 

    test_tri = [a,b,c] 

    if test_tri.min <=0 
    raise TriangleError 
    end 

    test_tri.sort! 

    if test_tri[0]+ test_tri[1] <= test_tri[2] 
    raise TriangleError 
    end 

    if a == b and b == c 
    :equilateral 
    elsif a != b and b != c and a != c 
    :scalene 
    else 
    :isosceles  
    end 
end 
3

Después de tratar de entender lo que debe hacer con koan 151, lo tengo con los primeros puestos, el entretenimiento y la gran cantidad de comprobar la solución a todos :) ... aquí es la mía:

def triangle(a, b, c) 
    array = [a, b, c].sort 
    raise TriangleError if array.min <= 0 || array[0]+array[1] <= array[2] 
    array.uniq! 
    array.length == 1 ? :equilateral: array.length == 2 ? :isosceles : :scalene 
end 

Koan es una forma muy interesante para aprender Rubí

0

el método del triángulo anterior debería aparecer aquí

class TriangleError < StandardError 
end 

def triangle(x,y,z) 
    if(x>=y+z||y>=x+z||z>=x+y) 
    raise TriangleError,"impossible triangle" 
    elsif(x==0&&y==0&&z==0)||(x<0||y<0||z<0) 
    raise TriangleError,"length cannot be zero or negative" 
    elsif(x==y&&x==z) 
    :equilateral 
    elsif(x==y||y==z||x==z) 
    :isosceles 
    else 
    :scalene 
    end 
end 
+0

hmm ... si es una respuesta, formatee el código para que sea legible – kleopatra

3

No creo que veo este de aquí, sin embargo.

Creo que todas las condiciones del triángulo ilegal implican que el lado más largo no puede ser más de la mitad del total. es decir:

def triangle(a, b, c) 

    fail TriangleError, "Illegal triangle: [#{a}, #{b}, #{c}]" if 
    [a, b, c].max >= (a + b + c)/2.0 

    return :equilateral if a == b and b == c 
    return :isosceles if a == b or b == c or a == c 
    return :scalene 

end 
0

Aquí está mi solución ... ¡sinceramente, no puedo pensar en una más concisa y legible!

def triangle(a, b, c) 
    raise TriangleError unless a > 0 && b > 0 && c > 0 
    raise TriangleError if a == b && a + b <= c 
    raise TriangleError if a == c && a + c <= b 
    return :equilateral if a == b && b == c 
    return :isosceles if a == b || b == c || c == a 
    :scalene 
end 
0

Reglas:

  1. tamaño debe ser> 0

  2. total de cualquiera de los 2 lados, debe ser más grande que la tercera Código

:

raise TriangleError if ([a,b,c].any? {|x| (x <= 0)}) or (((a+b)<=c) or ((b+c)<=a) or ((a+c)<=b)) 
[:equilateral, :isosceles, :scalene].fetch([a,b,c].uniq.size - 1) 
1
#(1)Any zero or -ve values 
    if [a,b,c].any? { |side_length| side_length <= 0 } 
    raise TriangleError 
    end 

    #(2)Any side of a triangle must be less than the sum of the other two sides 
    # a < b+c, b < a+c and c < a+b a valid triangle 
    # a >= b+c, b >= a+c and c >= a+b an invalid triangle 

    total_of_side_lengths = [a,b,c].inject {|total,x| total += x} 

    if [a,b,c].any? { |side_length| side_length >= (total_of_side_lengths - side_length)} 
    raise TriangleError 
    end 
0

No es necesario escribir el método TriangleError. Dice 'No hay necesidad de cambiar este código', así que no lo cambiaré en absoluto. Terco como soy.

4 líneas, nd limpio.

def triangle(a, b, c) 
     if(a * b * c <= 0) || (((a + c)<=b) || ((a + b)<=c)||((b + c)<=a)) 
      raise TriangleError else 
      return ((a == b && b == c && a == c)? :equilateral:(((a == b)||(b == c)||(a == c))? :isosceles: :scalene)) 
     end 
    end 


# Error class used in part 2. No need to change this code. 
class TriangleError < StandardError 
end 
0
def triangle(a, b, c) 
    raise TriangleError if [a, b, c].min <= 0 
    raise TriangleError if [a, b, c].max * 2 >= [a, b, c].reduce(:+) 
    if a == b && b == c 
    :equilateral 
    elsif a == b || b == c || c == a 
    :isosceles 
    else 
    :scalene 
    end 
end 
0

solución más pequeña que pudiera ocurrir.

Esto pasará las pruebas, sin embargo, no se quejará si la matriz es más larga de lo que debería. Fácil de probar pero no parecía completamente necesario.

  1. def(sides*) splat (a su vez la entrada en una matriz)
  2. raise TriangleError if ... or ... Elevar el error si cualquiera son verdaderas
  3. sides.sort! tipo la lista en su lugar. Esto es importante ya que dos declaraciones dependen de la lista que se está ordenando, lo que significa que solo se realiza una vez.
  4. sides.sort![0] <=0 obtener el elemento más pequeño, si esto es mayor que 0 todos los demás serán.
  5. sides.reverse.reduce(:-) >=0 elemento más grande menos los dos más pequeños, si esto es más de 0, entonces el lado más grande era más grande que los otros dos.
  6. sides.uniq.size-1 obtener el número de lados únicos (menos uno)
  7. [:e,:i,:s].fetch(sides.uniq.size-1) obtener el elemento apropiado de la matriz (y volver).

    def triangle(*sides) 
        raise TriangleError if sides.sort![0] <=0 or sides.reverse.reduce(:-) >=0 
        [:equilateral, :isosceles, :scalene].fetch(sides.uniq.size - 1) 
    end 
    
0

La comprobación de errores está tratando de llegar a los criterios de un triángulo:

if (a <= 0 || b <= 0 || c <= 0) 
    raise TriangleError,"All lengths must be positive" 
end 

args=[a,b,c].sort #this lets us get at the longest side of the triangle 

unless (args[0]+args[1] > args[2]) 
    raise TriangleError,"Longest length may not be greater than the sum of the other lengths" 
end 

if (args[0]-args[1] > args[2]) 
    raise TriangleError,"Longest length must be greater than the difference of the other two lengths" 
end 

Hay formas más elegantes y concisas que hacer este chequeo de error, pero creo que debería dejar claro cómo verificar los 4 principales criterios son. De hecho, lancé 2 criterios en un solo cheque. P.ej. ningún lado puede ser negativo y ningún lado puede ser 0.

Usar un operador de splat para los argumentos es una buena manera de asegurarse de tener una matriz sin formarla explícitamente, pero no me gusta aquí porque significa que también debería agregue un cheque para asegurarse de que hay exactamente 3 argumentos, que está implícito en este momento.

1

No es que esta pregunta necesite otra respuesta; sin embargo, creo que esta es la solución más simple y más fácil de leer. Gracias a todos los que me precedieron

def triangle(a, b, c) 
    a, b, c = [a, b, c].sort 
    raise TriangleError, "all sides must > 0" unless [a, b, c].min > 0 
    raise TriangleError, "2 smaller sides together must the > 3rd side" unless a + b > c 
    return :equilateral if a == b && a == c 
    return :isosceles if a == b || a == c || b == c 
    return :scalene 
end 

# Error class used in part 2. No need to change this code. 
class TriangleError < StandardError 
end 
1
def triangle(a, b, c) 

    sides = a, b, C# Assigns variable signs (array) to all arguments. 
    begin 

    raise TriangleError if sides.inject(:+) <= 0 # Raise an error if all sides added together are less than or equal to 0. (the triangle would be invalid). 
    raise TriangleError if sides.any?(&:negative?) #Raise an error if there are any negative sides. 
    sides.each {|side| (side < (sides.inject(:+) - side) ? nil : (raise TriangleError))} # For the final check, Raise an error if any single side is greater than the other two sides added together. It can be broken down like this if side is less than (remaining sides - side we're comparing) raise an error, else, nil. 

    return :equilateral if sides.uniq.length == 1 
    return :isosceles if sides.uniq.length == 2 
    return :scalene  if sides.uniq.length == 3 

    resuce TriangleError 
    end 
end 
+0

Explique qué hace su código. –

+0

Comentario de I second @ morten.c. También será útil si tiene comentarios en el código. –

+0

He agregado comentarios, avíseme si tiene más sentido. –

0

Ésta es mi solución: -

def triangle(a, b, c) 
    if a <= 0 || b <= 0 || c <= 0 || a + b <= c || a + c <= b || b + c <= a 
    raise TriangleError 
    elsif (a == b) &&(a==c) && (b==c) 
    return :equilateral 
    elsif (a==b) || (b==c) || (a==c) 
    return :isosceles 
    else 
    return :scalene 
    end 
end 

espero que ayude.

0

Puede utilizar esta fórmula y validar todos los casos:

  • a + b> c
  • un código + c> b
  • b + c> a

y como:

def triangle(a, b, c) 
    # Validate the triangle 
raise TriangleError, "The triangle is not valid" unless (a + b) > c && (a + c) > b && (b + c) > a 

if a === b && b === c 
    :equilateral 
elsif a === b || a === c || b === c 
    :isosceles 
else 
    :scalene 
end 
end 
Cuestiones relacionadas