Pregunta

En Tcl 8.5 Puedo hacer algo como esto:

apply llength { 1 2 3 }

Pero lo que corresponda no está definido en v8.4.

¿Cómo me aplico definir usando Tcl en v8.4?

Necesito esto porque estoy convirtiendo algo de código Lisp a Tcl. El código Lisp tiene algunas construcciones que me gustaría al puerto de esta manera:

array set levels {
  TRACE  0
  DEBUG  1
  INFO   2
  WARN   3
  ERROR  4
}

set LOG_LEVEL INFO
proc setLogLevel { level } {
  global LOG_LEVEL

  set LOG_LEVEL $level
}

proc log { tag msg args } {
  global levels
  global LOG_LEVEL

  # Filter out any messages below the logging severity threshold.
  if { $levels($LOG_LEVEL) <= $levels($tag) } then {
    apply format $msg $args
  }
}

proc logTrace { msg args } {
  apply log TRACE $msg $args
}
proc logDebug { msg args } {
  apply log DEBUG $msg $args
}
proc logInfo { msg args } {
  apply log INFO $msg $args
}
proc logWarn { msg args } {
  apply log WARN $msg $args
}
proc logError { msg args } {
  apply log ERROR $msg $args
}

# Close solution (not quite correct)
proc apply {func args} {
  eval [list $func] $args
}

# Example usage:
set instName "myInst"
set viewName "myView"
set cellName "myCell"
logError "Divide by zero."

# Filtered message:
logTrace "Evaluating callbacks for instance %s." $instName
# Enable that same message
setLogLevel TRACE
logTrace "Evaluating callbacks for instance %s." $instName

# This one fails with apply definition given here
logInfo "Opening cellView %s@%s." $viewName $cellName

Gracias.

William

¿Fue útil?

Solución

En función de su respuesta a mis comentarios a la pregunta original, recomiendo que aprenda a utilizar eval adecuadamente en lugar de tratar de crear una función que funciona de la forma que considera que debe aplicarse. Mi razonamiento es que si usted no entiende eval usted no tiene los conocimientos suficientes para entender cómo crear y utilizar el comando de aplicación.

Lo creas o no, su ejecución de la orden de aplicación es más o menos correcta, pero lo estaba utilizando de forma incorrecta. Para describir cómo y por qué utilizarlo correctamente, no vale la pena cuando hay otras maneras de resolver el problema.

Su problema se reduce a esto: que le den una función y N argumentos, y que necesita una manera de llamar a esa función con exactamente N argumentos. La solución adecuada para la que se va a utilizar eval.

Así es como me gustaría reescribir su función de registro. He tomado la libertad de añadir código para imprimir en realidad el resultado en lugar de calcular y devolverlo al igual que lo hizo su código. También he añadido código para imprimir el nivel de error:

proc log { tag msg args } {
  global levels
  global LOG_LEVEL

  # Filter out any messages below the logging severity threshold.
  if { $levels($LOG_LEVEL) <= $levels($tag) } then {
      set result [eval format \$msg $args]
      puts "$LOG_LEVEL: $result"
  }
}

Algunos puntos importantes para entender aquí. En primer lugar, la palabra 'args' es especial, y significa que todos los argumentos adicionales se recogen en una lista. Por lo tanto, si usted Registro de llamadas con cero argumentos, un argumento, o N argumentos, args es una lista y serán siempre una lista, incluso si se trata de una lista de cero o uno valora.

A medida que has descubierto, el comando format (potencialmente) necesita N argumentos en lugar de una lista de N argumentos. La forma de evitar esto en Tcl es utilizar la sentencia eval. La explicación simplista es que eval provoca una línea que se analiza dos veces.

Esto es bueno para $ args, ya que elimina de forma efectiva un nivel de "listness" - lo que era una lista de N elementos se convierte en artículos N distintas. Sin embargo, usted no quiere $ msg a ser analizada dos veces porque no es una lista de n elementos. Por eso hay una barra invertida delante de la $ - se esconde el signo de dólar desde el primer pase del analizador. Algunas personas prefieren [lista $ msg], y hay otras maneras de lograr la misma tarea.

(tenga en cuenta que en este caso específico con este código específico, no hay ningún problema en $ msg conseguir analizada dos veces. Es una buena práctica para proteger siempre cosas que no explícitamente desee ampliado cuando se utiliza eval, por razones que no vale la pena entrar en aquí ).

A continuación, tenemos que dirigir nuestra atención a las otras funciones de registro. Ellos funcionan de manera similar, y necesitan un tratamiento similar. Estos son todos esencialmente los comandos de paso a través, añadiendo un argumento adicional. Así es como loginfo debe ser, una vez más usando eval:

proc logInfo {msg args} {
    eval log INFO \$msg $args
}

Una vez más cuenta de que $ msg tiene una barra invertida delante de él. Esto es por la misma razón que el anterior - queremos que la ronda adicional de análisis por $ args pero no por $ mensaje.

Con estos dos cambios, el código funciona.

Sin embargo, hay una manera sin duda mejor para implementar las funciones logx. Puesto que todo lo que estamos haciendo es añadir un argumento extra y luego pasa todo lo demás tal cual a la función de registro que puede tomar ventaja de la capacidad del intérprete para crear alias. Por ejemplo:

interp alias {} logTrace {} log TRACE
interp alias {} logDebug {} log DEBUG
interp alias {} logInfo {} log INFO
interp alias {} logWarn {} log WARN
interp alias {} logError {} log ERROR

En el código anterior, las llaves significan simplemente "en el intérprete actual". TCL tiene la capacidad de tener múltiples intérpretes ejecutan, pero eso no es importante para el asunto en cuestión. Cuando se llama a logTrace, por ejemplo, Tcl realmente llamar 'Registro de seguimiento' y luego añadir ningún argumento adicional hasta el final. Así, 'valor logTrace foo' se convierte en 'log barra de TRACE foo'.

usted se refiere a portar una gran cantidad de código LISP a Tcl y quiere hacer el menor número de gimnasia mental como sea posible, lo cual es comprensible. Creo que es probablemente seguro decir, en su caso específico, siempre que vea aplica en el código LISP que sólo puede reemplazarlo con "eval". Luego tomar la medida adicional de protección de las cosas que no requieren ningún análisis adicional.

Otros consejos

Sorprendentemente, se llama aplicar en Tcl, así .

Estoy bastante seguro de que su solución se va a tener que utilizar eval, y tal vez uplevel también si quieres que funcione en procsos que utilizan uplevel o upvar.

Respuesta corta:

Si usted tiene el nombre del comando en una variable, se puede ejecutar mediante la colocación de la variable como la primera palabra de la línea:

set mycommand puts
$mycommand "hello world"

Respuesta larga:

Hay argumentos que desea ampliado, sin romper los casos extremos de su comando, por lo que puede utilizar eval para "reanálisis" la línea una vez que se crea. Básicamente, puede utilizar "eval" para expandir todos los argumentos, a continuación, utilizar "lista" para proteger a ciertas personas de la expansión

% proc {my llength of args} {args} { return [llength $args] }
% set mycommand {my llength of args}
% set args "1 2 3"
% eval $mycommand $args ;# Expands the command, so may blow up
ambiguous command name "my": {my llength} {my llength of args}
% eval [list $mycommand] [list $args] ;# protect the args, so it's not expanded, not what you want
1
% eval [list $mycommand] $args ;# Protect the things you don't want expanded (command named), but allow the args to be expanded to individual arguments
3
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top