第4章 数据类型:String、Int、Float、Boolean
WDL 的变量是有类型的。虽然 WDL 不像 Java 那样强制声明类型,但每个值的类型在运行时是固定的。我刚开始用 WDL 时吃过亏——以为它和 Python 一样随便赋值就行,结果被引擎报错教育了一顿。嗯,今天就把这四个基础类型掰开揉碎讲清楚。
4.1 Int:整数
Int 就是整数,支持正负。写法很直接:
Int count = 100
Int negative = -5
Int zero = 0
在 WDL 里,Int 是 64 位有符号整数。说白了就是范围±9e18够你用。我之前在项目里遇到过有人用 Int 存文件大小,结果算出来的值被截断了——因为他把浮点结果误赋给了 Int 变量。WDL 做隐式转换吗?不做。你强行把 Float 塞给 Int,引擎会报类型不匹配。
常用操作:
- 算术运算:+ - * / %
- 比较运算:== != < > <= >=
- 字符串拼接(Int 会被自动转成字符串再拼)
注意除法:两个 Int 相除结果还是 Int,小数部分直接截断,不四舍五入。
Int a = 7
Int b = 3
Int c = a / b # c = 2,而不是 2.333
4.2 Float:浮点数
Float 是双精度浮点数。写法:
Float pi = 3.14159
Float e = 2.71828
Float neg = -1.5
注意,WDL 里不能写 1.0f 或 1.0d 这种后缀。就写数字加小数点就行。整数后面不加小数点也是 Int,加了才是 Float。
有个坑:WDL 里 Float 精度够高,但做等号比较要小心。两个 Float 可能因为浮点误差不相等。我一般判断差值小于 1e-9 就算相等。
Float x = 0.1 + 0.2
Float y = 0.3
Boolean equal = (x == y) # 很可能为 false
项目里我常用 Float 做阈值控制,比如样本质量分数、时间成本计算。记住一点:WDL 不会自动把 Int 转成 Float,需要你显式声明或用函数转换。
4.3 Boolean:布尔值
Boolean 只有两个值:true 和 false。全小写。
Boolean is_done = true
Boolean has_error = false
布尔值通常用在条件分支和循环中。WDL 支持逻辑运算符:!(非)、&&(与)、||(或)。
if (is_done && !has_error) {
call RunReport
}
我见过有人写 if (flag == true),其实直接 if (flag) 就够。更简洁,也更符合习惯。
if (1) 表示真。只有显式的 Boolean 才能出现在条件表达式里。
4.4 String:字符串
String 用双引号包裹。支持转义符。
String name = "workflow"
String path = "/data/sample_001.fastq"
String multi = "line1\nline2"
字符串拼接用 +:
String prefix = "SRR"
String suffix = "001"
String id = prefix + suffix # "SRR001"
还有个好用的东西:字符串插值。用 ~{} 可以把变量、表达式直接嵌入字符串中。我个人特别喜欢这个功能,比拼接省事多了。
String sample = "NA12878"
String path = "/data/{~{sample}}.bam"
# path = "/data/NA12878.bam"
这里花括号里的 ~{} 是 WDL 的占位符,不依赖外部模板引擎。你甚至可以在里面放表达式:
Int year = 2025
String tag = "v~{year}_release"
f"{var}",WDL 不认。必须用 "~{var}"。
4.5 类型对比表格
我把四个类型的关键特征整理了一下,方便对照:
| 类型 | 取值范围/精度 | 字面量写法 | 隐式转换 | 常见用途 |
|---|---|---|---|---|
| Int | 64位有符号 | 42 |
不会隐式转Float | 计数、索引、ID |
| Float | 双精度 | 3.14 |
不会隐式转Int | 比例、阈值、时间 |
| Boolean | true/false | true |
不与其他类型互转 | 条件控制 |
| String | UTF-8 | "hello" |
其他类型可转String | 路径、标识、信息 |
注意最后一行——只有 String 可以接收其他类型的字面量拼接(自动转换),其他类型之间没有隐式转换。这也是我踩过的坑:想给一个 Float 变量赋整数值,直接写 3 是 Int 类型,赋值失败。得写 3.0 才行。
4.6 实际代码片段
下面是一个综合示例,用到了所有四种类型:
workflow TypeDemo {
input {
String sample_id = "sample_01"
Int read_count = 100000
Float quality_threshold = 20.5
Boolean enable_qc = true
}
command {
echo "Processing ~{sample_id} with ~{read_count} reads"
}
}
这里我刻意把变量和默认值绑在一起写。默认值是一个好习惯——即使输入端没给值,也能跑。不过注意默认值的类型必须和声明的类型一致。如果你写 Int read_count = "100000" ,引擎会报类型错误。
defined() 函数检查变量是否被赋值,这在处理可选输入时特别有用。例如:
if (defined(quality_threshold)) {
# 做质量控制
}
4.7 本章知识结构图
下面这张图梳理了四种基础类型的关系和转换规则。画布用淡蓝色,节点用不同的颜色区分类型,箭头表示允许的自动转换方向。