# Tcl 中的过程

> 原文： [https://zetcode.com/lang/tcl/procedures/](https://zetcode.com/lang/tcl/procedures/)

在本教程的这一部分中，我们将介绍 Tcl 过程。

过程是包含一系列命令的代码块。 在许多编程语言中，过程都称为函数。 对于程序仅执行一个特定任务，这是一种良好的编程习惯。 程序为程序带来了模块化。 正确使用程序会带来以下优点：

*   减少代码重复
*   将复杂的问题分解成更简单的部分
*   提高代码的清晰度
*   重用代码
*   信息隐藏

过程有两种基本类型：内置过程和用户​​定义的过程。 内置过程是 Tcl 核心语言的一部分。 例如，`rand()`，`sin()`和`exp()`是内置程序。 用户定义的过程是使用`proc`关键字创建的过程。

`proc`关键字用于创建新的 Tcl 命令。 术语过程和命令通常可以互换使用。

我们从一个简单的例子开始。

```tcl
#!/usr/bin/tclsh

proc tclver {} {

    set v [info tclversion]
    puts "This is Tcl version $v"
}

tclver

```

在此脚本中，我们创建一个简单的`tclver`过程。 该过程将打印 Tcl 语言的版本。

```tcl
proc tclver {} {

```

使用`proc`命令创建新过程。 `{}`字符表明该过程没有参数。

```tcl
    {

    set v [info tclversion]
    puts "This is Tcl version $v"
}

```

这是`tclver`过程的主体。 它在我们执行`tclver`命令时执行。 该命令的正文位于大括号之间。

```tcl
tclver

```

通过指定其名称来调用该过程。

```tcl
$ ./version.tcl 
This is Tcl version 8.6

```

样本输出。

## 过程参数

参数是传递给过程的值。 过程可以采用一个或多个参数。 如果过程使用数据，则必须将数据传递给过程。

在下面的示例中，我们有一个采用一个参数的过程。

```tcl
#!/usr/bin/tclsh

proc ftc {f} {

    return [expr $f * 9 / 5 + 32]
}

puts [ftc 100]
puts [ftc 0]
puts [ftc 30]

```

我们创建一个`ftc`程序，将华氏温度转换为摄氏温度。

```tcl
proc ftc {f} {

```

该过程采用一个参数。 该程序的主体将使用其名称`f`。

```tcl
return [expr $f * 9 / 5 + 32]

```

我们计算摄氏温度值。 `return`命令将值返回给调用方。 如果该过程未执行显式返回，则其返回值是在过程主体中执行的最后一条命令的值。

```tcl
puts [ftc 100]

```

执行`ftc`过程。 它以 100 为参数。 这是华氏温度。 返回的值由`puts`命令使用，该命令将其打印到控制台。

```tcl
$ ./fahrenheit.tcl
212
32
86

```

示例的输出。

接下来，我们将有一个接受两个参数的过程。

```tcl
#!/usr/bin/tclsh

proc maximum {x y} {

    if {$x > $y} {

        return $x
    } else {

        return $y
    }
}

set a 23
set b 32

set val [maximum $a $b]
puts "The max of $a, $b is $val"

```

`maximum`过程将两个值的最大值相减。

```tcl
proc maximum {x y} {

```

该方法有两个参数。

```tcl
if {$x > $y} {

    return $x
} else {

    return $y
}

```

在这里，我们计算哪个更大。

```tcl
set a 23
set b 32

```

我们定义了两个要比较的变量。

```tcl
set val [maximum $a $b]

```

我们计算两个变量的最大值。

```tcl
$ ./maximum.tcl
The max of 23, 32 is 32

```

这是`maximum.tcl`脚本的输出。

## 可变数量的参数

一个过程可以接受和处理可变数量的参数。 为此，我们使用特殊的`args`参数。

```tcl
#!/usr/bin/tclsh

proc sum {args} {

    set s 0

    foreach arg $args {

        incr s $arg
    }

    return $s
}

puts [sum 1 2 3 4]
puts [sum 1 2]
puts [sum 4]

```

我们定义一个`sum`过程，将其所有参数加起来。

```tcl
proc sum {args} {

```

`sum`过程具有一个特殊的`args`参数。 它具有传递给该过程的所有值的列表。

```tcl
foreach arg $args {

    incr s $arg
}

```

我们遍历列表并计算总和。

```tcl
puts [sum 1 2 3 4]
puts [sum 1 2]
puts [sum 4]

```

我们调用`sum`过程 3 次。 在第一种情况下，它采用 4 个参数，在第二种情况下为 2，在最后一种情况下为 4。

```tcl
$ ./variable.tcl
10
3
4

```

`variable.tcl`脚本的输出。

## 隐式参数

Tcl 过程中的参数可能具有隐式值。 如果未提供显式值，则使用隐式值。

```tcl
#!/usr/bin/tclsh

proc power {a {b 2}} {

    if {$b == 2} {
        return [expr $a * $a]
    }

    set value 1

    for {set i 0} {$i<$b} {incr i} {

        set value [expr $value * $a]
    }

    return $value
}

set v1 [power 5]
set v2 [power 5 4]

puts "5^2 is $v1"
puts "5^4 is $v2"

```

在这里，我们创建一个`power`过程。 该过程具有一个带有隐式值的参数。 我们可以使用一个和两个参数来调用该过程。

```tcl
proc power {a {b 2}} {

```

第二个参数`b`具有隐式值 2。如果仅提供一个参数，则`power`过程会将`a`的值返回到幂 2。

```tcl
set v1 [power 5]
set v2 [power 5 4]

```

我们用一个和两个参数调用幂过程。 第一行计算 5 的幂 2。第二行计算 5 的幂 4。

```tcl
$ ./implicit.tcl
5^2 is 25
5^4 is 625

```

示例的输出。

## 返回多个值

`return`命令将一个值传递给调用方。 通常需要返回多个值。 在这种情况下，我们可以返回一个列表。

```tcl
#!/usr/bin/tclsh

proc tworandoms {} {

    set r1 [expr round(rand()*10)]
    set r2 [expr round(rand()*10)]

    return [list $r1 $r2]
}

puts [tworandoms]
puts [tworandoms]
puts [tworandoms]
puts [tworandoms]

```

我们有一个`tworandoms`程序。 它返回 1 到 10 之间的两个随机整数。

```tcl
set r1 [expr round(rand()*10)]

```

计算一个随机整数并将其设置为`r1`变量。

```tcl
return [list $r1 $r2]

```

借助`list`命令返回两个值。

```tcl
$ ./tworandoms.tcl 
3 7
1 3
8 7
9 9

```

样本输出。

## 递归

在数学和计算机科学中，递归是一种定义函数的方法，其中所定义的函数在其自己的定义内应用。 换句话说，递归函数调用自身以完成其工作。 递归是解决许多编程任务的一种广泛使用的方法。 递归是诸如 Scheme，OCalm 或 Clojure 之类的函数式语言的基本方法。

递归调用在 Tcl 中有一个限制。 递归调用不能超过 1000 个。

递归的典型示例是阶乘的计算。 阶乘`n!`是所有小于或等于 n 的正整数的乘积。

```tcl
#!/usr/bin/tclsh

proc factorial n {

    if {$n==0} {

        return 1
    } else {

        return [expr $n * [factorial [expr $n - 1]]]
    }
}

# Stack limit between 800 and 1000 levels

puts [factorial 4]
puts [factorial 10]
puts [factorial 18]

```

在此代码示例中，我们计算三个数字的阶乘。

```tcl
return [expr $n * [factorial [expr $n - 1]]]

```

在`factorial`过程的主体内部，我们使用修改后的参数调用`factorial`过程。 该过程将自行调用。

```tcl
$ ./recursion.tcl
24
3628800
6402373705728000

```

这些就是结果。 如果我们尝试计算 100 的阶乘，则会收到“嵌套求值过多”的错误。

## 作用域

在过程内部声明的变量具有过程作用域。 名称的作用域是程序文本的区域，在该区域内可以引用名称声明的实体而无需对该名称进行限定。 在过程内部声明的变量具有过程作用域； 它也称为本地作用域。 该变量仅在此特定过程中有效。

```tcl
#!/usr/bin/tclsh

proc test {} {

    puts "inside procedure"
    #puts "x is $x"
    set x 4
    puts "x is $x"
}

set x 1

puts "outside procedure"
puts "x is $x"

test

puts "outside procedure"
puts "x is $x"

```

在前面的示例中，我们在测试过程的内部和外部定义了一个`x`变量。

```tcl
set x 4
puts "x is $x"

```

在测试过程中，我们定义了`x`变量。 该变量具有局部作用域，仅在此过程内有效。

```tcl
set x 1

puts "outside procedure"
puts "x is $x"

```

我们在过程外部定义`x`变量。 它具有全球作用域。 变量没有冲突，因为它们具有不同的作用域。

```tcl
$ ./scope.tcl
outside procedure
x is 1
inside procedure
x is 4
outside procedure
x is 1

```

示例的输出。

可以在过程内部更改全局变量。

```tcl
#!/usr/bin/tclsh

proc test {} {

    upvar x y
    puts "inside procedure"
    puts "y is $y"
    set y 4
    puts "y is $y"
}

set x 1

puts "outside procedure"
puts "x is $x"

test

puts "outside procedure"
puts "x is $x"

```

我们定义一个全局`x`变量。 我们在测试过程中更改变量。

```tcl
upvar x y

```

我们使用`upvar`命令通过名称`y`引用全局`x`变量。

```tcl
set y 4

```

我们为本地`y`变量分配一个值，并更改全局`x`变量的值。

```tcl
$ ./scope2.tcl
outside procedure
x is 1
inside procedure
y is 1
y is 4
outside procedure
x is 4

```

从输出中我们可以看到测试过程更改了`x`变量。

使用`global`命令，我们可以从过程中引用全局变量。

```tcl
#!/usr/bin/tclsh

proc test {} {

    global x 
    puts "inside test procedure x is $x"

    proc nested {} {
        global x
        puts "inside nested x is $x"
    }
}

set x 1

test
nested

puts "outside x is $x"

```

在上面的示例中，我们有一个测试过程以及在该测试过程中定义的嵌套过程。 我们从这两个过程中都引用了全局`x`变量。

```tcl
global x 
puts "inside test procedure x is $x"

```

使用`global`命令，可以引用在测试过程之外定义的全局`x`变量。

```tcl
proc nested {} {
    global x
    puts "inside nested x is $x"
}

```

可以创建嵌套过程。 这些是在其他过程中定义的过程。 我们使用`global`命令引用全局`x`变量。

```tcl
test
nested

```

我们称之为测试过程及其嵌套过程。

```tcl
$ ./scope3.tcl
inside test procedure x is 1
inside nested x is 1
outside x is 1

```

示例的输出。

在 Tcl 教程的这一部分中，我们介绍了 Tcl 过程。