HoopLang Docs
/
Playground
El libro · Capítulo 08

8. Repetir y decidir

Ocho rayos de un sol, doce lunares, una plancha de parches: el bordado está lleno de repeticiones. Y de decisiones: si el nombre es largo, la letra va más angosta; si la tela es oscura, el borde va más fino. En este capítulo aprendes for para repetir e if para decidir.

for sobre un rango#

HoopLang
for i in 0..12 {
  fill oro { shape: rayo.rotate(i * 30deg, centro) }
}

0..12 son los enteros del 0 al 11: el último no se incluye. Doce vueltas, con i valiendo 0, 1, 2… 11. Dentro, i es un Int y se usa como cualquier valor: i * 30deg es un Angle, i * 9mm un Length.

El truco de .rotate(ángulo, centro) del capítulo 2 es lo que hace girar cada copia alrededor del mismo punto, en vez de alrededor de su propio centro.

Los límites del rango son enteros (pueden ser expresiones o param, como 0..puntas en la estrella del capítulo 7):

Salida del compilador
t.hoop:4:3: error E001: a range is Int..Int (for i in 0..4)

for sobre una lista#

Una lista se escribe entre corchetes: [2mm, 3mm, 4mm], ["ANA", "LUIS"]. Un for la recorre elemento por elemento:

HoopLang
const tamanos = [2mm, 3mm, 4mm, 5mm, 4mm, 3mm]

layer lunares {
  for r in tamanos {
    fill rojo { shape: circle(center: (65mm, 15mm), r: r) }
  }
}

Y con dos nombres, for x, i in …, te da también el índice (desde 0):

HoopLang
for r, i in tamanos {
  fill rojo { shape: circle(center: (65mm + i * 9mm, 15mm), r: r) }
}

len(lista) es la cantidad de elementos, y len(texto) la cantidad de caracteres (una "Á" cuenta como uno):

HoopLang
// no-check Plantilla sintáctica con elipsis, no un programa ejecutable.
for i in 0..len(tamanos) { … }

Algunas reglas:

  • Un for va en una capa (y puede contener primitivas, inside, place, if y otros for) o en un hoop (con place, capítulo 9). No va dentro de un seq.

  • Hasta 1000 vueltas:

    Salida del compilador
    t.hoop:4:3: error E002: 2000 iterations: a loop goes up to 1000
  • La variable no puede tapar otro nombre:

    Salida del compilador
    t.hoop:4:3: error E007: the loop variable `rojo` hides another name: choose another
  • Un error dentro de un for se informa una vez, no una por vuelta. Aquí sólo la tercera vuelta se sale del lienzo, y verás un solo error:

    Salida del compilador
    vuelta.hoop:5:5: error E006: fill overflows the canvas by 9.0 mm on the right

grid: copias en filas y columnas#

Para ubicar copias en una grilla, grid calcula el punto de la celda i, de izquierda a derecha y de arriba abajo:

HoopLang
grid(i, cols: 4, step: (12mm, 12mm), start: (65mm, 35mm))
  • i: el número de celda (un Int, normalmente la variable del for).
  • cols: cuántas columnas tiene cada fila.
  • step: la distancia entre celdas, (horizontal, vertical).
  • start: el punto de la celda 0 (por defecto, (0mm, 0mm)).

Con cols: 4, la celda 5 es la segunda de la segunda fila: (65mm + 1 × 12mm, 35mm + 1 × 12mm).

if: decidir#

HoopLang
if i mod 2 == 0 {
  fill rojo { shape: circle(center: (65mm + i * 9mm, 15mm), r: r) }
} else {
  fill azul { shape: circle(center: (65mm + i * 9mm, 15mm), r: r) }
}

if condición { … } else if otra { … } else { … }: el else es opcional. Va donde va un for: en capas y en un hoop.

La condición es un Bool. Se arma con:

  • comparaciones ==, !=, <, <=, >, >= entre valores del mismo tipo (< y compañía sólo entre números: Int, Length, Angle, Ratio);
  • and, or y not entre Bool;
  • true y false.
Salida del compilador
t.hoop:4:10: error E001: cannot compare Length with Angle
Salida del compilador
t.hoop:2:14: error E001: a condition is a Bool (a comparison like len(name) > 8), not Int

mod: uno sí, uno no#

a mod b es el resto de dividir a por b, entre enteros. Es la manera de decir "cada dos", "cada tres": i mod 2 == 0 es verdadero en las vueltas pares, i mod 5 == 0 en una de cada cinco.

if como valor#

Un if también puede dar un valor, dentro de una propiedad. Entonces lleva else, y las dos ramas dan el mismo tipo:

HoopLang
size: if i mod 5 == 0 { (9mm, 9mm) } else { (6mm, 6mm) }
HoopLang
// error E001
design "Tipos" { canvas: rect(size: (20mm,20mm)) }
thread negro = #000000
layer a { fill negro { shape: rect(size: if true { 3mm } else { 2 }) } }
Salida del compilador
t.hoop:3:42: error E001: both branches of an if give the same type, not Length and Int

Las dos ramas se verifican#

El compilador revisa las dos ramas, aunque sólo borde la elegida. Un error en la rama que hoy no se toma se informa igual:

Salida del compilador
rama.hoop:5:5: error E003: undeclared thread `b`; did you mean `a`?

Es a propósito: el día que un param cambie y se tome la otra rama, no quieres descubrir el error en la máquina.

Un diseño con patrones#

HoopLang
// repetir.hoop: un sol de rayos, una fila de lunares y una grilla.
design "Patrones" {
  canvas: rect(size: (120mm, 80mm))
}

thread oro  = #d8a21b "Oro"
thread rojo = #cc3f24 "Rojo"
thread azul = #1f3a8a "Azul"

const centro = (30mm, 30mm)
shape rayo = polygon((28mm, 18mm), (30mm, 6mm), (32mm, 18mm))
const tamanos = [2mm, 3mm, 4mm, 5mm, 4mm, 3mm]

layer sol {
  fill oro { shape: circle(center: centro, r: 10mm) }
  // Doce rayos: el mismo triángulo, girado alrededor del centro.
  for i in 0..12 {
    fill oro { shape: rayo.rotate(i * 30deg, centro) }
  }
}

layer lunares {
  // Uno rojo, uno azul: el resto de dividir por 2 decide.
  for r, i in tamanos {
    if i mod 2 == 0 {
      fill rojo { shape: circle(center: (65mm + i * 9mm, 15mm), r: r) }
    } else {
      fill azul { shape: circle(center: (65mm + i * 9mm, 15mm), r: r) }
    }
  }
}

layer grilla {
  // Doce cuadritos en 4 columnas; los de la diagonal, más grandes.
  for i in 0..12 {
    fill azul {
      shape: rect(at: grid(i, cols: 4, step: (12mm, 12mm), start: (65mm, 35mm)),
        size: if i mod 5 == 0 { (9mm, 9mm) } else { (6mm, 6mm) })
    }
  }
}
Salida del compilador
patrones.hoop: 107.0 × 62.0 mm, 4417 stitches, 3 threads, 29 trims, 28 ms

29 cortes: cada lunar y cada cuadrito es una isla, y la máquina corta entre islas. En un diseño real, piensa si las islas pueden unirse en una forma (+), o si un corrido escondido bajo otra capa puede llevar el hilo de una a otra. Menos islas, menos cortes.

param + if: componentes que se adaptan#

Lo más útil de if aparece con los param del capítulo 7. Esta etiqueta elige la fuente según el largo del nombre, y el fondo y el borde según un Bool:

HoopLang
// etiqueta.hoop: el nombre decide su tamaño, su forma y su color.
param nombre = "MARÍA JOSÉ"
param oscuro = false

thread blanco = #f0f0f0 "Blanco"
thread azul   = #1f3a8a "Azul"
thread negro  = #1c1616 "Negro"

design "Etiqueta" { canvas: rect(size: (70mm, 30mm), radius: 5mm) }

shape caja = rect(at: (6mm, 5mm), size: (58mm, 20mm))

layer fondo {
  if oscuro {
    fill azul { shape: canvas }
  } else {
    fill blanco { shape: canvas }
  }
}

layer texto {
  if len(nombre) > 8 and nombre != "" {
    text negro { value: nombre, font: Bebas, box: caja } // largo: angosta
  } else if len(nombre) > 4 {
    text negro { value: nombre, font: ArialBold, box: caja }
  } else {
    text negro { value: nombre, font: ArialBold, height: 12mm, at: (35mm, 21mm), align: center }
  }
}

layer borde {
  satin negro { border: canvas, width: if oscuro { 2mm } else { 3mm } }
}

Y quien la coloca decide:

HoopLang
design "E" { canvas: rect(size: (160mm, 40mm)) }
layer a {
  place "etiqueta.hoop"(nombre: "ANA", oscuro: true) at (40mm, 20mm)
  place "etiqueta.hoop"(nombre: "VALENTINA") at (120mm, 20mm)
}
Salida del compilador
etiquetas.hoop: ok — 3 threads, 1 layers, 2 objects

(Este archivo no declara hilos: los 3 threads del resumen son los colores que se cosen, que vienen con las etiquetas, contados como los cuenta hoop build.)

Resumen#

  • for i in a..b recorre enteros de a a b - 1; for x in [ … ] una lista; for x, i in … da también el índice.
  • grid(i, cols:, step:, start:) ubica la celda i de una grilla.
  • if … { } else if … { } else { } decide; como valor, lleva else y ramas del mismo tipo.
  • Comparaciones entre valores del mismo tipo; and, or, not; mod es el resto entre enteros; len cuenta caracteres o elementos.
  • Las dos ramas de un if se verifican; un error en un for se informa una vez.

Referencia: control · expresiones · tipos