summaryrefslogtreecommitdiffhomepage
path: root/es-es/tcl-es.html.markdown
blob: f1835ef234db2b943c783e800aa652bb44735a48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
---
language: Tcl
contributors:
    - ["Poor Yorick", "https://pooryorick.com/"]
translators:
    - ["Héctor Romojaro", "https://github.com/hromojaro"]
lang: es-es
filename: learntcl-es.tcl
---

Tcl fue creado por [John Ousterhout](https://wiki.tcl.tk/John%20Ousterout) como
un lenguaje reutilizable de scripting para herramientas de diseño de circuitos
de las que él era autor.  En 1997 recibió el 
[ACM Software System Award](https://en.wikipedia.org/wiki/ACM_Software_System_Award) 
por Tcl.   Tcl puede ser utilizado tanto como lenguaje de scripting embebido, 
como lenguaje de programación general.  Puede ser utilizado también como una
biblioteca portable de C, incluso en casos donde no se requieren capacidades
de scripting, ya que provee de estructuras de datos tales como cadenas (*string*)
de caracteres dinámicas, listas y tablas hash.  La biblioteca de C también
provee funcionalidad portable para cargar bibliotecas dinámicas, formato de
cadenas y conversión de código, operaciones sobre el sistema de ficheros,
operaciones de red y más.  Algunas características reseñables de Tcl:

* Conveniente API de red multiplataforma

* Sistema de ficheros totalmente virtualizado

* Canales apilables de E/S

* Asíncrono hasta el núcleo

* Corrutinas completas

* Un modelo de hebras reconocido como robusto y fácil de usar


Tcl tiene mucho en común con Lisp pero, en lugar de listas, Tcl utiliza cadenas
de caracteres como moneda de cambio del lenguaje.  Todos los valores son cadenas.
Una lista es una cadena con un formato definido, y el cuerpo de un procedimiento
(un script) es también una cadena en lugar de un bloque.  Para incrementar el
rendimiento, Tcl cachea internamente representaciones estructuradas de estos
valores.  Las rutinas con listas, por ejemplo, operan en la representación interna
en caché, y Tcl se ocupa de actualizar la representación en cadenas si es realmente
necesario en el script.  El diseño *copy-on-write* de Tcl permite a los autores
de scripts mover grandes volúmenes de datos sin incurrir en el consumo adicional
de memoria.  Los procedimientos son automáticamente compilados (*byte-compiled*)
a no ser que utilicen rutinas dinámicas como "uplevel", "upvar" o "trace".

Programar en Tcl es un placer.  Le resultará atractivo a hackers que encuentren
atractivo Lisp, Forth o Smalltalk, y a ingenieros y científicos que simplemente
quieren ponerse a trabajar con una herramienta que se doblega a su voluntad.  La
disciplina de exponer toda la funcionalidad programática como rutinas, incluyendo
cosas como iteraciones y operaciones matemáticas que normalmente están en la
sintaxis de otros lenguajes, permitiendo fundirse en el fondo de cualquier
funcionalidad específica del dominio que necesita un proyecto.  Su sintaxis,
incluso más simple que la de lisp, simplemente se quita de en medio.



```tcl
#! /bin/env tclsh

###############################################################################
## 1. Directrices
###############################################################################

# ¡Tcl no es ni Sh ni C!  Es necesario decirlo porque el entrecomillado estándar
# de shell casi funciona en Tcl, y es común que la gente empiece con Tcl e
# intente utilizar sintaxis de otros lenguajes.  Funciona al principio, pero
# rápidamente conduce a frustración cuando los scripts se vuelven más complejos.

# Las llaves son un mecanismo de entrecomillado, no de sintaxis para la construcción
# de bloques de código o listas.  Tcl no tiene ninguna de ellas.  Las llaves se
# usan para escapar caracteres especiales, lo que las hace apropiadas para 
# entrecomillar cuerpos de procedimientos y cadenas que deberían ser interpretadas
# como listas.


###############################################################################
## 2. Sintaxis
###############################################################################

# Un script consiste en comandos delimitados por saltos de línea o puntos y coma.
# Cada comando es una llamada a una rutina.  La primera palabra es el nombre de
# la rutina a llamar, y las siguientes palabras son argumentos de la rutina.
# Las palabras están delimitadas por espacios.  Puesto que cada argumento es una
# palabra en el comando, y una cadena de caracteres, puede no ser entrecomillada:
set part1 Sal
set part2 ut; set part3 ations


# el símbolo del dólar introduce la sustitución de variables:
set greeting $part1$part2$part3


# Cuando "set"recibe sólamente el nombre de una variable, devuelve su valor:
set part3 ;# Returns the value of the variable.


# Los corchetes delimitan un script que será evaluado y sustituido por su resultado:
set greeting $part1$part2[set part3]


# Un script incrustado puede estar compuesto de múltiples comandos, el último de
# los cuales devuelve el resultado de la sustitución:
set greeting $greeting[
    incr i
    incr i
    incr i
]
puts $greeting ;# La salida es "Salutations3"

# Cada palabra en un comando es una cadena, incluyendo el nombre de la rutina,
# así que se pueden utilizar sustituciones allí también. Dada esta asignación
# de variable,

set action pu

# los siguientes tres comandos son equivalentes:
puts $greeting
${action}ts $greeting 
[set action]ts $greeting


# La barra invertida suprime el significado especial de los caracteres:
set amount \$16.42


# La barra invertida añade significado especial a ciertos caracteres:
puts lots\nof\n\n\n\n\n\nnewlines


# Una palabra encerrada entre llaves no está sujeta a interpretación especial o
# sustitución, excepto que una barra invertida antes de una llave no cuenta al
# buscar la llave de cierre:
set somevar {
    This is a literal $ sign, and this \} escaped
    brace remains uninterpreted
}


# En una palabra delimitada por comillas dobles, los espacios pierden su significado
# especial:
set name Neo
set greeting "Hello, $name"


# Un nombre de variable puede ser cualquier cadena:
set {first name} New


# La forma de sustitución de variables utilizando llaves permite nombres de
# variable más complejos:
set greeting "Hello, ${first name}"


# "set" puede utilizarse siempre en lugar de la sustitución de variables, y permite
# utilizar cualquier nombre de variable:
set greeting "Hello, [set {first name}]"


# Para desempaquetar una lista en un el comando, se utiliza el operador de expansión,
# "{*}".  Estos dos comandos son equivalentes:
set name Neo
set {*}{name Neo}


# Un array es una variable especial que sirve como contenedor de otras variables.
set person(name) Neo
set person(destiny) {The One}
set greeting "Hello, $person(name)"


# "variable" se puede utilizar para declarar o asignar variables. Al contrario
# que "set", que utiliza el espacio de nombres global y el actual para resolver
# un nombre de variable, "variable" usa solamente el actual:
variable name New


# "namespace eval" crea un nuevo espacio de nombres en caso de no existir.
# Un espacio de nombres puede contener tanto rutinas como variables:
namespace eval people {
    namespace eval person1 {
        variable name Neo
    }
}


# Use dos o más ":" para delimitar componentes del espacio de nombres en nombres
# de variables:
namespace eval people {
    set greeting "Hello $person1::name"
}

# Dos o más ":" también delimitan componentes del espacio de nombres en nombres
# de rutinas:
proc people::person1::speak {} {
    puts {I am The One.}
}

# Nombres completos comienzan con dos ":":
set greeting "Hello $::people::person1::name"



###############################################################################
## 3. No más sintaxis
###############################################################################

# El resto de funcionalidades se implementa mediante rutinas.  Desde este punto,
# no hay nueva sintaxis.  Todo lo que queda para aprender Tcl es acerca del
# comportamiento de rutinas individuales y el significado que asignan a sus
# argumentos.



###############################################################################
## 4. Variables y espacios de nombres
###############################################################################

# Cada variable y cada rutina están asociadas a algún espacio de nombres

# Para terminar con un intérprete inútil, sólo hay que eliminar el espacio de
# nombres global.  No es algo muy útil, pero sirve para ilustrar la naturaleza
# de Tcl.  El nombre del espacio de nombres global es en realidad la cadena
# vacía, pero la única forma de representarlo es como un nombre completo. Para
# probarlo, se puede usar esta rutina.
proc delete_global_namespace {} {
    namespace delete ::
}

# Como "set" siempre mantiene su vista en los espacios de nombres global y actual,
# es más seguro utilizar "variable" para declarar o asignar un valor a una
# variable.  Si una variable llamada "nombre" ya existe en el espacio de nombres
# global, usar "set" asignará un valor a la variable local en lugar de a la
# variable del espacio de nombres actual, mientras que "variable" opera en el
# espacio de nombres actual solamente.
namespace eval people {
    namespace eval person1 {
        variable name Neo
    }
}

# Una vez que una variable es declarada en un espacio de nombres, [set] la vé
# en lugar de una variable de idéntico nombre en el espacio de nombres global:
namespace eval people {
    namespace eval person1 {
        variable name
        set name Neo
    }
}

# En cambio, si "set" tiene que crear una nueva variable, siempre lo hace en el
# espacio de nombres actual:
unset name
namespace eval people {
    namespace eval person1 {
        set name neo
    }

}
set people::person1::name


# Un nombre absoluto siempre comienza con el nombre del espacio de nombres global
# (cadena vacía), seguido de dos ":":
set ::people::person1::name Neo


# En el interior de un procedimiento, la variable enlaza una variable en el espacio
# de nombres actual en el ámbito local:
namespace eval people::person1 {
    proc fly {} {
        variable name
        puts "$name is flying!"
    }
}




###############################################################################
## 4. Rutinas incorporadas
###############################################################################

# Las operaciones matemáticas se pueden hacer con "expr":
set a 3
set b 4
set c [expr {$a + $b}]

# Como "expr" realiza sustituciones de variables por sí mismo, es necesario
# poner la expresión entre llaves para prevenir a Tcl sustituir las variables
# primero. Ver "http://wiki.tcl.tk/Brace%20your%20#%20expr-essions" para más
# detalles.


# "expr" entiende sustitución de variables y scripts:
set c [expr {$a + [set b]}]


# "expr" provee de un conjunto de funciones matemáticas:
set c [expr {pow($a,$b)}]


# Los operadores matemáticos están disponibles como rutinas en el espacio de
# nombres ::tcl::mathop
::tcl::mathop::+ 5 3

# Las rutinas pueden ser importadas desde otros espacios de nombres:
namespace import ::tcl::mathop::+
set result [+ 5 3]


# Los valores no numéricos deben ser entrecomillados, y los operadores como "eq"
# pueden utilizarse para restringir la operación a una comparación de cadenas:
set name Neo
expr {{Bob} eq $name}

# Los operadores generales recurren a la comparación de cadenas si una operación
# numérica no es factible.
expr {{Bob} == $name}


# "proc" crea nuevas rutinas:
proc greet name {
    return "Hello, $name!"
}

# Se pueden especificar múltiples parámetros:
proc greet {greeting name} {
    return "$greeting, $name!"
}


# Como se dijo antes, las llaves no construyen un bloque de código.  Cada valor,
# incluso el tercer argumento de "proc", es una cadena.  El comando anterior
# puede ser reescrito sin usar llaves:
proc greet greeting\ name return\ \"\$greeting,\ \$name!\"



# Cuando el último parámetro es el valor literal "args", todos los argumentos
# extra pasados a la rutina son recogidos en una lista y asignado a "args":
proc fold {cmd first args} {
    foreach arg $args {
        set first [$cmd $first $arg]
    }
    return $first
}
fold ::tcl::mathop::* 5 3 3 ;# ->  45


# La ejecución condicional se implementa como una rutina:
if {3 > 4} {
    puts {This will never happen}
} elseif {4 > 4} {
    puts {This will also never happen}
} else {
    puts {This will always happen}
}


# Los bucles se implementan como rutinas.  Los primer y tercer argumentos de "for"
# son tratados como scripts, mientras que el segundo lo es como una expresión:
set res 0
for {set i 0} {$i < 10} {incr i} {
    set res [expr {$res + $i}]
}
unset res


# El primer argumento de "while" se trata también como una expresión:
set i 0
while {$i < 10} {
    incr i 2
}


# Una lista es una cadena, y los elementos de la lista se delimitan con espacios
# en blanco:
set amounts 10\ 33\ 18
set amount [lindex $amounts 1]

# El espacio en blanco dentro de una lista debe ser entrecomillado:
set inventory {"item 1" item\ 2 {item 3}}


# Generalmente, es mejor idea usar rutinas de listas al modificarlas:
lappend inventory {item 1} {item 2} {item 3}


# Las llaves y barras invertidas pueden utilizarse para formatear valores más
# complejos en una lista.  Una lista parece un script, excepto en que el carácter
# de nueva línea y el ":" pierden su significado especial, y no hay sustitución
# de variable o scripts.  Esta característica hace Tcl homoicónico.  Hay tres
# elementos en la siguiente lista:
set values {

    one\ two

    {three four}

    five\{six

}


# Como, al igual que todos los valores, una lista es una cadena, operaciones de
# cadenas pueden ser realizadas sobre ellas, corriendo el riesgo de corromper
# el formato de la lista:
set values {one two three four}
set values [string map {two \{} $values] ;# $values is no-longer a \
    properly-formatted list


# La forma segura de conseguir una lista debidamente formateada es utilizando
# las rutinas propias de lista:
set values [list one \{ three four]
lappend values { } ;# add a single space as an item in the list


# Se puede utilizar "eval" para evaluar un valor como un script:
eval {
    set name Neo
    set greeting "Hello, $name"
}


# Una lista siempre puede ser pasada a "eval" como un script compuesto de un único
# comando:
eval {set name Neo}
eval [list set greeting "Hello, $name"]


# Por lo tanto, cuando se utiliza "eval", use "list" para construir el comando
# deseado:
set command {set name}
lappend command {Archibald Sorbisol}
eval $command


# Un error común es no usar funciones de listas al construir un comando:
set command {set name}
append command { Archibald Sorbisol}
try {
    eval $command ;# El error es que "set" tiene demasiados argumentos en \
        {set name Archibald Sorbisol}
} on error {result eoptions} {
    puts [list {received an error} $result]
}

# Este error puede ocurrir fácilmente con "subst":

set replacement {Archibald Sorbisol}
set command {set name $replacement}
set command [subst $command] 
try {
    eval $command ;# El mismo error que antes:  demasiados argumentos a "set" en \
        {set name Archibald Sorbisol}
} trap {TCL WRONGARGS} {result options} {
    puts [list {received another error} $result]
}


# "list" formatea correctamente un valor para su sustitución:
set replacement [list {Archibald Sorbisol}]
set command {set name $replacement}
set command [subst $command]
eval $command


# "list" se utiliza normalmente para formatear valores para su sustitución en
# scripts: Hay muchos ejemplos de esto más abajo.


# "apply" evalúa una lista de dos elementos como una rutina:
set cmd {{greeting name} {
    return "$greeting, $name!"
}}
apply $cmd Whaddup Neo

# Un tercer elemento puede ser utilizado para especificar el espacio de nombres
# donde aplicar la rutina:
set cmd [list {greeting name} {
    return "$greeting, $name!"
} [namespace current]]
apply $cmd Whaddup Neo


# "uplevel" evalúa un script en un nivel superior de la pila de llamadas:
proc greet {} {
    uplevel {puts "$greeting, $name"}
}

proc set_double {varname value} {
    if {[string is double $value]} {
        uplevel [list variable $varname $value]
    } else {
        error [list {not a double} $value]
    }
}


# "upvar" enlaza una variable en el nivel actual de la pila de llamadas a una
# variable en un nivel superior:
proc set_double {varname value} {
    if {[string is double $value]} {
        upvar 1 $varname var
        set var $value
    } else {
        error [list {not a double} $value]
    }
}


# Deshacerse de la rutina "while" incorporada, y utilizar "proc" para definir
# una nueva:
rename ::while {}
# la manipulación se deja como ejercicio:
proc while {condition script} {
    if {[uplevel 1 [list expr $condition]]} {
        uplevel 1 $script
        tailcall [namespace which while] $condition $script
    }
}


# "coroutine" crea una nueva pila de llamadas, una nueva rutina en la que
# introducir esa pila de llamadas, y luego llama a dicha rutina. "yield" suspende
# la evaluación en esa pila y devuelve el control a la pila que efectúa la llamada.
proc countdown count {
    # devuelve algo al creador de la corrutina, efectivamente pausando esta
    # pila de llamadas por ahora.
    yield [info coroutine]

    while {$count > 1} {
        yield [incr count -1]
    }
    return 0
}
coroutine countdown1 countdown 3
coroutine countdown2 countdown 5
puts [countdown1] ;# -> 2 
puts [countdown2] ;# -> 4 
puts [countdown1] ;# -> 1 
puts [countdown1] ;# -> 0 
catch {
    puts [coundown1] ;# -> invalid command name "countdown1"
} cres copts 
puts $cres
puts [countdown2] ;# -> 3 


# Pilas de corrutinas pueden cederse el control entre sí:

proc pass {whom args} {
    return [yieldto $whom {*}$args]
}

coroutine a apply {{} {
        yield
        set result [pass b {please pass the salt}]
        puts [list got the $result]
        set result [pass b {please pass the pepper}]
        puts [list got the $result]
}}

coroutine b apply {{} {
    set request [yield]
    while 1 {
        set response [pass c $request]
        puts [list [info coroutine] is now yielding]
        set request [pass a $response]
    }
}}

coroutine c apply {{} {
    set request [yield]
    while 1 {
        if {[string match *salt* $request]} {
            set request [pass b salt]
        } else {
            set request [pass b huh?]
        }
    }
}}

# Pon las cosas en marcha
a
```

## Reference

[Documentación oficial de Tcl](http://www.tcl.tk/man/tcl/)

[Tcl Wiki](http://wiki.tcl.tk)

[Tcl Subreddit](http://www.reddit.com/r/Tcl)