4. 数据类型入门:Int、Float、Boolean、String、File 与使用场景
数据类型这东西,说实话,在编程里是最基础的砖块。但在 WDL 里,它又有点自己的小脾气。我个人习惯在写工作流的第一步,先把输入输出类型定死,这样后面能少踩很多坑。
WDL 提供了五种基础数据类型,分别是:Int(整形)、Float(浮点型)、Boolean(布尔型)、String(字符串)和 File(文件)。今天我们就一个个把它们理清楚。
4.1 从 Int 和 Float 开始:数字的世界
Int 就是整数,Float 就是带小数点的数字。这俩在 WDL 里的用法跟其他语言差不多,但有个细节你一定要注意:WDL 里的 Float 必须显式地写成带小数点的形式。你写 5 就是 Int,写 5.0 才是 Float。
我刚开始写 WDL 时,习惯性地把 3.14 赋给一个 Int 变量,结果 Cromwell 直接报类型不匹配。看个例子:
workflow TypesDemo {
Int count = 10
Float threshold = 0.75
call MyTask {
input:
n = count,
t = threshold
}
}
task MyTask {
input {
Int n
Float t
}
command {
echo "count is ~{n}, threshold is ~{t}"
}
output {
String out = read_string(stdout())
}
}
Int x = 5 + 3.2 是允许的,结果会变成 Float。但反过来,你把 Float 传给 Int 参数就会失败。
4.2 Boolean:对与错的开关
Boolean 只有两个值:true 和 false。它通常用在条件判断里,控制工作流的执行分支。说白了,就是“如果……就……”的逻辑。
我记得有次在项目里,需要根据样本类型来决定要不要再做二次质控。这个开关就是用 Boolean 来控制的。代码长这样:
workflow QCSelector {
Boolean run_deep_qc = true
if (run_deep_qc) {
call DeepQC {}
}
call BasicQC {}
}
if (condition) {} 里直接使用。条件判断不需要用 == true,直接写变量名就行。想取反就用 ! 符号。
4.3 String:文本的容器
String 是万能的,什么文本都能往里放。但在 WDL 里,String 有一个很实用的特性:变量插值。你可以用 ~{变量名} 的写法,把变量塞进字符串里。
举个例子,给样本生成文件名:
workflow Naming {
String sample_id = "SAMPLE_001"
String out_prefix = "results/~{sample_id}/aligned"
call Align {
input:
prefix = out_prefix
}
}
task Align {
input {
String prefix
}
command {
echo "output will be ~{prefix}.bam"
}
}
你看,~{sample_id} 会被替换成实际的值。这比手动拼接字符串方便多了。
~{} 和 WDL 表达式里的 ${} 是不一样的。在 command {} 块里,必须用 ~{} 来插值。我曾经因为搞混这两个符号,调试了整整一下午。
4.4 File:WDL 的灵魂
File 类型是 WDL 的核心。它代表一个文件路径,可以是本地路径,也可以是云端路径(如 gs://bucket/file.fastq)。Cromwell 会自动处理文件的传输和映射,你只管把 File 传进传出的就好。
但有个坑很多人会掉进去:File 只是一个路径引用,不是文件内容本身。你不能直接对 File 做字符串操作,比如用 read_string() 才能读取内容。
看个实际案例:
workflow FileExample {
File raw_data = "gs://my-bucket/input.fastq"
String file_name = basename(raw_data)
call TrimGalore {
input:
fastq = raw_data,
prefix = file_name
}
}
task TrimGalore {
input {
File fastq
String prefix
}
command {
trim_galore --fastqc ~{fastq} -o ~{prefix}_trimmed.fastq
}
output {
File trimmed = "~{prefix}_trimmed.fastq"
}
}
4.5 一张图理清全部关系
这五种数据类型之间的关系和使用场景,我画了张图,一目了然:
4.6 类型系统里的几个潜规则
WDL 的类型系统虽然简单,但也有几条潜规则你必须知道。我当年就是没注意这些,被坑过好几次。
| 规则 | 说明 | 示例/反例 |
|---|---|---|
| 隐式类型转换 | Int 可以隐式转成 Float,但 Float 不能隐式转成 Int | Float f = 5 ✓ Int i = 5.0 ✗ |
| String 传文件 | String 可以写路径,但不会触发文件传输机制 | File 会自动处理云端拉取,String 不会 |
| Boolean 和 if 块 | if 块里不能直接使用 Int 或 String 做条件判断 | if (1) {} ✗ 必须用 Boolean |
| File 的不可变性 | File 变量一旦赋值,内部路径不能修改 | 要改路径就新建一个 String 拼接后再赋值 |
4.7 实战组合:一个完整的混合类型案例
最后,给你看一个我实际项目中用过的片段,把今天学的五种类型串起来:
workflow AlignmentPipeline {
File reference_fasta
File input_fastq
String sample_name
Int threads = 8
Float min_quality = 20.0
Boolean do_duplicate_marking = true
call Align {
input:
ref = reference_fasta,
reads = input_fastq,
sample = sample_name,
cpu = threads
}
if (do_duplicate_marking) {
call MarkDuplicates {
input:
bam = Align.aligned_bam
}
}
}
这个工作流里,File、String、Int、Float、Boolean 全用上了。你看,它们各司其职,组合起来就是一条完整的数据处理链条。说白了,类型系统就是给工作流定规矩,让 Cromwell 知道该拿什么数据、做什么处理、产什么结果。
Int 和 String 把逻辑参数定好,再用 File 把数据传输框架搭起来,最后用 Boolean 控制流程分支。按这个顺序来,很少出错。