Nextflow 是在 Groovy 编程语言之上实现的领域特定语言(Domain Special Language,DSL) ,而 Groovy 编程语言又是 Java 编程语言的超集。这意味着 Nextflow 可以运行任何 Groovy 和 Java 代码。学习 Groovy 使用 Nextflow DSL 是没有必要的,但是在需要比 DSL 提供更多功能的边缘情况下,它可能会很有用。
语言基础
打印值
println("Hello, World!")
println "Hello, World!"
函数调用的括号是可选的。
变量
在任何编程语言中,都需要使用变量来存储不同类型的信息。变量是指向计算机内存中存储与其相关的值的空间。
变量使用 = 赋值,并且可以具有任何值。Groovy 是动态类型化的,这意味着变量的数据类型是基于它的值的。例如,设置 x = 1意味着 x 是一个整数,但是如果它被设置为 x = “ hello”,那么它就变成了一个 String。
变量作用域
当我们使用 x = 1语法创建一个变量时,我们可以在脚本中的任何地方(全局)访问。以这种方式声明的变量有时称为公共变量。
我们还可以使用数据类型来定义变量,例如 String x = “ Hello”或者使用 def 关键字 def x = 1。这会影响变量的作用范围。这称为词法作用域(有时称为静态作用域) ,它设置变量的作用域,以便只能从定义变量的代码块中访问它。以这种方式声明的变量有时称为私有变量。
数据类型
String
- 以括在引号中的字符串的形式表示。例如“ Hello World”。int
- 表示整数的,例如1234。Boolean
- 布尔值,它可以是 true 也可以是 false。float
- 表示浮点数12.34。
参考链接
//int − This is used to represent whole numbers.
my_var = 1
//float − This is used to represent floating point numbers.
my_var = 3.1499392
//Boolean − This represents a Boolean value which can either be true or false.
my_var = false
//String - These are text literals which are represented in the form of chain of characters
my_var = "chr1"
多行字符串
跨越多行的文本块可以通过使用三重单引号或双引号来定义:
text = """
This is a multi-line string
using triple quotes.
"""
字符串打印
要在单行或多行双引号字符串中使用变量,在变量名前面加上一个 $:
chr = "1"
println("processing chromosome $chr") //processing chromosome 1
chr = "1"
println('processing chromosome $chr') //processing chromosome $chr
列表
要在一个变量中存储多个值,我们可以使用 List。List (也称为 array)对象可以通过将列表项放在方括号中并用逗号分隔来定义。
可以使用方括号符号[]访问列表中的给定项。这些位置从0开始编号,因此第一个元素的索引为0。
kmers = [11,21,27,31]
println(kmers[0])
我们可以在 Groovy 中使用负数作为索引。它们从列表的末尾而不是前面开始计数: 索引 -1给出列表中的最后一个元素,-2给出倒数第二个元素,依此类推。因此,kmers [3]和 kmers [-1]指向示例列表中的同一个元素。
kmers = [11,21,27,31]
//Lists can also be indexed with negative indexes
println(kmers[3])
println(kmers[-1])
列表也可以使用范围进行索引。使用<num1>..<num2>
:
kmers = [11,21,27,31]
// The first three elements using a range.
println(kmers[0..2])
// [11, 21, 27]
在一个字符串中药使用kmers[0..2]
表达式,要使用${expression}
,和Bash类似。
kmers = [11,21,27,31]
println("The first three elements in the Lists are. $kmers[0..2]")
// The first three elements in the Lists are. [11, 21, 27, 31][0..2]
kmers = [11,21,27,31]
println("The first three elements in the Lists are. ${kmers[0..2]}")
//The first three elements in the Lists are. [11, 21, 27]
列表方法
列表中有很多操作列表项的方法详见。
mylist = [0,1,2]
println(mylist.size())
//inside a string need we need to use the ${} syntax
println("list size is: ${mylist.size()}")
mylist = [0,1,2]
println mylist.get(1)
mylist = [1,2,3]
println mylist
println mylist + [1]
println mylist - [1]
println mylist * 2
println mylist.reverse()
println mylist.collect{ it+3 }
println mylist.unique().size()
println mylist.count(1)
println mylist.min()
println mylist.max()
println mylist.sum()
println mylist.sort()
println mylist.find{it%2 == 0}
println mylist.findAll{it%2 == 0}
字典(Maps)
可以使用 Groovy Maps (也称为关联数组) ,它具有任意类型的键,而不是整数值。注意: 键值没有用引号括起来。
可以使用传统的方括号语法访问Map,或者将键作为属性,或者使用点符号。注意: 检索值时,键值用引号括起来。
roi = [ chromosome : "chr17", start: 7640755, end: 7718054, genes: ['ATP1B2','TP53','WRAP53']]
//Use of the square brackets.
println(roi['chromosome'])
//Use a dot notation
println(roi.start)
//Use of get method
println(roi.get('genes'))
要添加数据或修改映射,语法类似于向列表中添加值:
//Use of the square brackets
roi['chromosome'] = '17'
//Use a dot notation
roi.chromosome = 'chr17'
//Use of put method
roi.put('genome', 'hg38')
闭包(Closures)
闭包是 Nextflow/Groovy 的瑞士军刀。简而言之,闭包是可以作为参数传递给函数的代码块。这对于创建可重用的函数非常有用。
我们可以使用=将一个闭包分配给一个变量: square = { it * it }
。
it
是闭包中提供的一个隐式变量。
我们可以将函数 square 作为参数传递给其他函数或方法。一些内置函数将这样的函数作为参数。一个例子是列表上的 collect
方法,它循环遍历列表中的每个元素,使用闭包将其转换为一个新值:
square = { it * it }
x = [ 1, 2, 3, 4 ]
y = x.collect(square)
println y
闭包也可以以匿名的方式定义:
x = [ 1, 2, 3, 4 ]
y = x.collect({ it * it })
println("x is $x")
println("y is $y")
闭包默认有一个it
参数,当然也可以自定义参数名称,例如:square = { num -> num * num }
。
还可以使用-> 语法定义包含多个自定义参数的闭包。例如:
tp53 = [chromosome: "chr17",start:7661779 ,end:7687538, genome:'GRCh38', gene: "TP53"]
//perform subtraction of end and start coordinates
region_length = {start,end -> end-start }
tp53.length = region_length(tp53.start,tp53.end)
println(tp53)
另一个例子是,应用于 map 的 each ()方法可以带有两个参数的闭包,它将 map 对象中每个条目的键-值对传递给该闭包:
//closure with two parameters
printMap = { a, b -> println "$a with value $b" }
//map object
my_map = [ chromosome : "chr17", start : 1, end : 83257441 ]
//each iterates through each element
my_map.each(printMap)
条件表达式
if
任何编程语言最重要的特性之一就是能够在不同的条件下执行不同的代码。最简单的方法是使用 if。
if( < boolean expression > ) {
// true branch
}
else {
// false branch
}
else 分支是可选的。当分支只定义一条语句时,花括号是可选的。
x = 12
if( x > 10 )
println "$x is greater than 10"
null、空字符串和空集合为 false:
list = [1,2,3]
if( list != null && list.size() > 0 ) {
println list
}
else {
println 'The list is empty'
}
等价于:
if( list )
println list
else
println 'The list is empty'
在某些情况下,可以用三元表达式(也称为条件表达式)替换 if 语句。例如: println list ? list : 'The list is empty'
,还可以进一步简化为println list ?: 'The list is empty'
。
for
for (int i = 0; i <3; i++) {
println("Hello World $i")
}
list = ['a','b','c']
for( String elem : list ) {
println elem
}
函数
可以在脚本中定义一个自定义函数,如下所示:
int fib(int n) {
return n < 2 ? 1 : fib(n-1) + fib(n-2)
}
println (fib(10)) // prints 89