Powershell 数据容器:数组、ArrayList 与 哈希表

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本文介绍 Powershell 语言中的主要数据容器: 数组、ArrayList 与 哈希表

Powershell数据容器:数组、ArrayList 与 哈希表


李俊才(jcLee95) 的个人博客
已入驻阿里云
邮箱 :291148484@163.com
本文地址
- https://developer.aliyun.com/article/
- https://blog.csdn.net/qq_28550263/article/details/125723259

目 录


1. 数组

2. ArrayList

3. 哈希表


1. 数组

Powershell 中,提供了数组,其类型为 System.Array。这个原生数组功能并不完善,仅在某些不是很复杂的脚本中使用。更多时候,我们会使用 下一小节 中所介绍的 ArrayList。

1.1 创建 Powershell 数组

使用@(...)(…表示任意的元素)语法可以创建 Powershell 数组,例如:

$a = @(0,1,2,3,4,5,6)

在上例中,这个数组一共有7个元素,分别是1,2,3,4,5,6。如果你需要创建的是一个空数组,不列写任何元素即可,如:

$a = @()

1.2 向数组中添加元素

Poweshell数组有Add()方法可以为数组添加一个元素,这个元素将添加到数组的末尾,并使数组的长度加1。但是对于使用@()语法创建的数组是 固定长度数组,因此如果按照下面这种方法来插入元素会报错:

$a = @();
$a.Add(1);

Out[]:

MethodInvocationException: Exception calling “Add” with “1” argument(s): “Collection was of a fixed size.”

由于原数组是没有办法再修改其长度了,因此如果你一定要向数组中添加元素,这时只能通过重新创建一个包含所有元素以及新元素的数组来模拟数组中添加元素。因此 你可以参考 1.7 合并两个数组 小节中使用 + 运算符的方式来构建新数组。事实上,当 + 运算符右侧为非数组元素时,它会视为只有一个元素的数组,采用数组合并的逻辑来返回一个全新的数组。

1.3 索引数组元素

Powershell 数组提供了 Get()方法用以索引数组中某个元素的值,例如:

$a = @(0,1,2,3,4,5,6);
$a.Get(3);

Out[]:3

这和以下索引书写方式的效果是一样的:

$a = @(0,1,2,3,4,5,6);
$a[3];

Out[]:3

1.4 修改数组中的元素

使用Set()方法可以用来修改数组中的元素,例如:

$a = @(0,1,2,3,4,5,6);
 $a.SetValue(2,9);
 $a

Out[]:

0
1
9
3
4
5
6

可以看到,数组的第3个元素(索引为2)由3变成了9

还有一种更简单的方法是使用索引该值,和$a.SetValue(2,9)等价的做法是$a[2] = 9;,即:

$a = @(0,1,2,3,4,5,6);
$a[2] = 9;
$a

Out[]:

0
1
9
3
4
5
6

1.5 数组切片

从语法上看,数组切片与数组的索引类似,都是使用[]来获取原数组的一部分。但是数组的切片返回的不是一个元素,而是数组中一段索引连续的元素构成的新数组。例如:

$a = @(0,1,2,3,4,5,6);
$a[3..6];    # 获取数组 $a 中,从 $a[3] 到 $a[6] 的所有元素构成的新数组

Out[]:

3
4
5
6

1.6 删除数组中的元素

你无法使用 RemoveRemoveAt 方法来移除 Powershell 数组中的元素,因为 Powershell 数组是一个固定大小的集合。因此就地删除元素是不可能的,但这不妨碍我们构建一个没有被删除元素的新数组来模拟数组元素的删除。

# 依据索引模拟数组元素删除
# 规定使用 -1 表示移除最后一个元素
function Remove([int]$index, [System.Array]$ary){
    $length = $ary.Count;
    if($index -gt $length -1){Write-Error 索引超出范围}
    elseif($index -lt -1){Write-Error 索引必须大于-1}
    else{
        # 删除索引为 -1 相当于删除索引为 length-1 的元素,其余留下
        if($index -eq -1){
            return $ary[0..($length-2)]
        }
        # 删除的是第一个元素,其余留下
        elseif($index -eq 0){
            return $ary[1..($length-1)]
        }
        # 删除中间索引号为index的元素,其余留下
        else {
            return ($ary[0..($index-1)] + $ary[($index+1)..($length-1)])
        }
    }
}

按照以下方法来调用:

# 删除最后一个元素
$a = @(0,1,2,3,4,5,6,7);
Remove -index -1 -ary $a

Out[]:

0
1
2
3
4
5
6

【说明】:Powershell 数组完全支持负数索引,但我个人更习惯仅仅使用 -1 获取最后数组的一个元素。

1.7 合并两个数组

1.7.1 使用 + 运算符

若要将两个数组合并为单个数组,请使用加号运算符 (+) 。 以下示例创建两个数组,将其合并,然后显示生成的组合数组。例如:

$a=@(1,2,3);
$b=@(4,5,6);
$c = $a+$b;

其返回结果为包含两个数组所有元素,即数组 c 是由 1、2、3、4、5、6 构成的新数组。

1.7.2 使用 += 运算符

你也可以使用 += 来合并数组,例如:

$a=@(1,2,3);
$b=@(4,5,6);
$a+=$b

这相当于新创建了一个数组 c ,其元素为 1、2、3、4、5、6,在将数组 c 中的元素赋值给数组a,因此a的元素为 1、2、3、4、5、6。注意这里并不是对原数组 a 进行就地修改。

1.8 判断数组是否包含某个元素

$a=@(1,2,3);
$a.Contains(2);

Out[]: True

1.9 复制数组

数组是对象,可以使用Clone()方法对其实现浅复制,例如:

$a = @(0,1,2,3,4,5,6,7);
$b = $a.Clone();

这样 $b 就成立 $a 的一个浅复制副本。这里顺便提一下,在Powershell 中,万物皆对象,就像在 Python 这样的面向对象语言中一样。将表示一个对象的变量直接赋值给表示另一个对象的变量,仅仅是使被赋值的变量引用到赋值变量相同的对象的地址,因此这样的赋值方式不会获得一个全新的对象。例如:

$a = @(0,1,2,3,4,5,6,7);
$b = $a;
$b[2]=6;
$a

Out[]:

0
1
6
3
4
5
6
7

可以看到,虽然我们使用$b[2]=6;而不是$a[2]=6;,但是$a的索引为2的元素却被修改了——这里的根本原因是$b = $a;仅仅将$a引用的对象的地址传递给了$b

1.10 枚举数组

$a = @(0,1,2);
$b = $a.GetEnumerator();
$b

Out[]:

0
1
2
$b.GetType()

Out[]:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
False    False    ArrayEnumerator                          System.Object

1.11 数组元素类型约束

[int[]] $intA = 1, 2, 3;
$intA[1] = "A";

这将导致报错,因为限制了必须为 int 数组,不能再将元素该为字符串:

InvalidArgument: Cannot convert value “A” to type “System.Int32”. Error: “Input string was not in a correct format.”

1.12 多维数组平展

1.13 关于数组表达式

数组表达式的语法格式为:

@( new-lines~opt~ statement-list~opt~ new-lines~opt~ )

1.14 Powershell 数组 的成员

成员名 类型 描述
Item ParameterizedProperty 返回数组对应的 System.Management.Automation.PSMethodInfo 对象
Count Property 返回表示数组长度的整数。
IsFixedSize Property 返回一个 bool 值,该值指示 Array 是否具有固定大小
IsReadOnly Property 返回一个 bool 值,该值指示 Array 是否只可读取
IsSynchronized Property 返回一个 bool 值,该值指示是否同步对 Array 的访问(线程安全)
Length Property 返回表示数组长度的整数,和 Count 属性效果一样
LongLength Property 获取一个 64 位整数,该整数表示 Array 的所有维数中元素的总数。
Rank Property 获取 Array 的秩(维数)。 例如,一维数组返回 1,二维数组返回 2,依次类推。
SyncRoot Property 获取可用于同步对 Array 的访问的对象。
Add Method 向数组尾插元素
Address Method
Clear Method 清除数组的内容为默认值,不会改变数组的长度。若指定的为索引范围,它将数组中的某个范围的元素设置为每个元素类型的默认值。
Clone Method 创建 Array 的浅表副本
CompareTo Method
Contains Method 判断数组中是否包含某元素
CopyTo Method 将当前一维数组的所有元素复制到指定的一维数组中。
Equals Method
Get Method 接受一个整数作为数组索引,返回索引结果。
GetEnumerator Method 获取对象的枚举值。
GetHashCode Method 用于返回当前对象的哈希代码。
GetLength Method 获取数组在指定维度上的长度。如: $a = @(0,1,2,3,4,5,6); $a.GetLength(0)的返回值为 7
GetLongLength Method 获取一个整数,该整数表示 Array 的指定维中的元素数
GetLowerBound Method 获取数组中指定维度第一个元素的索引:int GetLowerBound (int dimension)。
GetType Method 获取此对象的类型,返回的是一个对象。对于数组来说,返回的 BaseType字段 为 System.Array。
GetUpperBound Method 获取数组中指定维度第一个元素的索引
GetValue Method 用于索引数组中某个位序地元素
IndexOf Method 求元素在数组中第一次出现的位序号(即索引值),若元素不出现,则返回-1
Initialize Method 初始化数组。
Insert Method 向数组的指定索引处插入一个元素。
Remove Method void IList.Remove(System.Object value)
RemoveAt Method 移除指定索引处的元素:void IList.RemoveAt(int index)。Powershell 的数组实际上不可使用该方法,原因是原生Powershell数组为定长数组,为一种长度固定的集合(Collection was of a fixed size)。
Set Method 将当前 Array 中的指定元素设置为指定值。void Set(int , System.Object)
SetValue Method 将当前 Array 中的指定元素设置为指定值。void SetValue(System.Object value, int index)
ToString Method 转换为字符串,对于 Powershell 数组来说,默认下总是返回 “System.Object[]

2. ArrayList

2.1 如何使用 ArrayList

你应该感受到了 powershell 原生数组的诸多麻烦,即使是元素的增加和删除都显得特别费劲。但是好在powershell 是可以使用 .Net 的。因此完全可以使用 System.Collections.ArrayList 让我们繁琐的操作简单化。

要创建一个ArrayList数组,你大概需要多写一些:

$a = [System.Collections.ArrayList]@(1,2,3,4,5,6);
$a

Out[]:

1
2
3
4
5
6

不过,往往在程序中为了方便,我们会使用 using 预先导入 System.Collections,这时使用 ArrayList 会显得更加便捷:

using namespace System.Collections;
[ArrayList]@(1,2,3,4,5,6);

2.2 ArrayList 的常用属性和方法

名称 类型 描述
Capacity 属性 获取或设置 ArrayList 可包含的元素数。
Count 属性 获取 ArrayList 中实际包含的元素数。
IsFixedSize 属性 获取一个值,该值指示 ArrayList 是否具有固定大小。
IsReadOnly 属性 获取一个值,该值指示 ArrayList 是否为只读。
IsSynchronized 属性 获取一个值,该值指示是否同步对 ArrayList 的访问(线程安全)。
Item[Int32] 属性 获取或设置指定索引处的元素。
SyncRoot 属性 获取可用于同步对 ArrayList 的访问的对象。
Adapter(IList) 方法 为特定 IList 创建 ArrayList 包装。
Add(Object) 方法 将对象添加到 ArrayList 的结尾处。
AddRange(ICollection) 方法 将 ICollection 的元素添加到 ArrayList 的末尾。
BinarySearch(Int32, Int32, Object, IComparer) 方法 使用指定的比较器在已排序 ArrayList 的某个元素范围中搜索元素,并返回该元素从零开始的索引。
BinarySearch(Object) 方法 使用默认的比较器在整个已排序的 ArrayList 中搜索元素,并返回该元素从零开始的索引。
BinarySearch(Object, IComparer) 方法 使用指定的比较器在整个已排序的 ArrayList 中搜索元素,并返回该元素从零开始的索引。
Clear() 方法 从 ArrayList 中移除所有元素。
Clone() 方法 创建 ArrayList 的浅表副本。
Contains(Object) 方法 确定某元素是否在 ArrayList 中。
CopyTo(Array) 方法 从目标数组的开头开始,将整个 ArrayList 复制到兼容的一维 Array。
CopyTo(Array, Int32) 方法 从目标数组的指定索引处开始将整个 ArrayList 复制到兼容的一维 Array。
CopyTo(Int32, Array, Int32, Int32) 方法 从目标数组的指定索引处开始,将 ArrayList 中某个范围的元素复制到兼容的一维数组 Array。
Equals(Object) 方法 确定指定对象是否等于当前对象。
FixedSize(ArrayList) 方法 (继承自 Object) 返回具有固定大小的 ArrayList 包装。
FixedSize(IList) 方法 返回具有固定大小的 IList 包装。
GetEnumerator() 方法 返回用于整个 ArrayList 的枚举数。
GetEnumerator(Int32, Int32) 方法 返回 ArrayList 中元素范围的枚举器。
GetHashCode() 方法 作为默认哈希函数。
GetRange(Int32, Int32) 方法 (继承自 Object)返回一个 ArrayList,它表示源 ArrayList 中的元素子集。
GetType() 方法 获取当前实例的 Type。
IndexOf(Object) 方法 (继承自 Object)搜索指定的 Object,并返回整个 ArrayList 中第一个匹配项的从零开始的索引。
IndexOf(Object, Int32) 方法 搜索指定的 Object,并返回 ArrayList 中从指定索引到最后一个元素的元素范围中第一个匹配项的从零开始索引。
IndexOf(Object, Int32, Int32) 方法 搜索指定的 Object,并返回 ArrayList 中从指定索引开始,并包含指定元素数的元素范围中第一个匹配项的从零开始的索引。
Insert(Int32, Object) 方法 将元素插入 ArrayList 的指定索引处。
InsertRange(Int32, ICollection) 方法 将集合中的元素插入 ArrayList 的指定索引处。
LastIndexOf(Object) 方法 在整个 ArrayList 中搜索指定的 Object,并返回最后一个匹配项的从零开始的索引。
LastIndexOf(Object, Int32) 方法 搜索指定的 Object,并返回 ArrayList 中从第一个元素到指定索引这部分元素中最后一个匹配项的从零开始索引。
LastIndexOf(Object, Int32, Int32) 方法 搜索指定的 Object,并返回 ArrayList 中到指定索引为止包含指定元素数的这部分元素中最后一个匹配项的从零开始的索引。
MemberwiseClone() 方法 创建当前 Object 的浅表副本。
ReadOnly(ArrayList) 方法 (继承自 Object)返回只读的 ArrayList 包装。
ReadOnly(IList) 方法 返回只读的 IList 包装。
Remove(Object) 方法 从 ArrayList 中移除特定对象的第一个匹配项。
RemoveAt(Int32) 方法 移除 ArrayList 的指定索引处的元素。
RemoveRange(Int32, Int32) 方法 从 ArrayList 中移除一系列元素。
Repeat(Object, Int32) 方法 返回 ArrayList,其元素是指定值的副本。
Reverse() 方法 将整个 ArrayList 中元素的顺序反转。
Reverse(Int32, Int32) 方法 将指定范围中元素的顺序反转。
SetRange(Int32, ICollection) 方法 复制 ArrayList 中一个子集合的元素。
Sort() 方法 对整个 ArrayList 中的元素进行排序。
Sort(IComparer) 方法 使用指定的比较器对整个 ArrayList 中的元素进行排序。
Sort(Int32, Int32, IComparer) 方法 使用指定的比较器对 ArrayList 中某个范围内的元素进行排序。
Synchronized(ArrayList) 方法 返回同步的(线程安全)ArrayList 包装器。
Synchronized(IList) 方法 返回同步的(线程安全)IList 包装器。
ToArray() 方法 将 ArrayList 的元素复制到新 Object 数组中。
ToArray(Type) 方法 将 ArrayList 的元素复制到新的指定元素类型数组中。
ToString() 方法 返回表示当前对象的字符串。
TrimToSize() 方法 (继承自 Object) 将容量设置为 ArrayList 中元素的实际数目。

2.3 例子

2.3.1 向 ArrayList 尾插元素

使用Add()方法可以向 ArrayList 尾插元素,例如:

using namespace System.Collections;
$a = [ArrayList]@();
for($i=0; $i -lt 3; $i++){
    $a.Add($i);
}
$a

Out[]:

0
1
2

2.3.2 替换 ArrayList 中的元素

using namespace System.Collections;
$a = [ArrayList]@(1,2,3,4,5,6);
$a[3] = 9;
$a;

Out[]:

1
2
3
9
5
6

2.3.3 移除 ArrayList 中的元素

using namespace System.Collections;

2.3.4 索引 ArrayList 中的元素

using namespace System.Collections;

2.3.5 反转 ArrayList 中的元素

ArrayList 提供了 Reverse() 方法进行就地翻转。例如:

using namespace System.Collections;
$a = [ArrayList]@(1,2,3,4,5,6);
$a.Reverse();
$a

Out[]:

6
5
4
3
2
1

如果你不希望就地反转,而是生成一个副本,可以参考如下的方式:

using namespace System.Collections;
$a = [ArrayList]@(1,2,3,4,5,6);
$b = $a.Clone();
$b.Reverse();
$b

Out[]:

6
5
4
3
2
1

这样就不会改变变量 $a 的值。

2.3.6 对 ArrayList 切片

using namespace System.Collections;

2.3.7 按照元素值返回索引

和 Powershell 数组的索引方式一样,如:

using namespace System.Collections;
[ArrayList]$a = @(0,1,2,3,4);
$a[2..3];

Out[]:

2
3

2.3.8 在指定位置插入元素

using namespace System.Collections;

2.3.9 清空 ArrayList

using namespace System.Collections;

2.3.10 对 ArrayList 元素进行排序

using namespace System.Collections;

3. 哈希表

3.1 哈希表的特点

有时我们需要哈希表来完成更多的功能,这是一种由若干键值对构建而成的数据结构。在一个哈希表中,没有重复的键,但不同键对应的值是可以相同的。其他语言中提供的 字典(Dict)、映射(Map) 都是哈希表。(注意:数据结构领域英文 Map 翻译过来是映射,而非初中英语单词 “地图”)。

3.2 Powershell 哈希表的用法

3.2.1 哈希表的创建

哈希表的创建语法和数组很像,只是使用@{}(数组是@())。例如:

$a = @{
    aa = 1;
    bb = 2;
    cc = "3";
};
$a

Out[]:

Name                           Value
----                           -----
bb                             2
aa                             1
cc                             3

3.2.2 访问哈希表

哈希表的访问就像查字典,在有些编程语言里面提供的散列表(哈希表)结构的原生数据容器也因此就叫字典(dict)。访问哈希表即访问其中的元素,是通过 键(key) 来获取其中的 值(value) 的。例如:

$a = @{
    aa = 1;
    bb = 2;
    cc = "3";
};
$a.bb;

Out[]:2

3.2.3 为哈希表添加键值对

你可以直接为一个字典键赋一个值,以此来新增字符串。例如:

$a = @{
    aa = 1;
    bb = 2;
    cc = "3";
};
$a.dd = 4;    # 添加一个键值对 {dd = 4}
$a

Out[]:

Name                           Value
----                           -----
dd                             4
cc                             3
bb                             2
aa                             1

3.2.4 修改哈希表指定键的值

修改哈希表的方法与哈希表新增键值对的方法完全一样,只不过是当指定的键不纯在时则为新增键值对,若已经存在则为修改键值对。例如:

$a = @{
    aa = 1;
    bb = 2;
    cc = "3";
};
$a.aa = 'a';
$a;

Out[]:

Name                           Value
----                           -----
cc                             3
bb                             2
aa                             a

3.2.5 删除哈希表中的键值对

使用 Remove(key)方法可以删除哈希表中的键值对,例如:

$a = @{
    aa = 1;
    bb = 2;
    cc = "3";
};
$a.Remove('aa');
$a;

Out[]:

Name                           Value
----                           -----
cc                             3
bb                             2

可以看到,键值对 { aa = 1} 顺利从该哈希表中删除。

3.3 将两个数组映射为 Powershell 哈希表

假设有两个长度相等的数组,一个数组中的所有元素用作键,另一个数组中的所有元素用作值,键和值在两个数组中的位序时意义对应的,以此生成一个哈希表,这时在编程中很使用的功能。以下是实现上述目标的一个参考方法:

function zip {
    param (
        [System.Array]$ary1, [System.Array]$ary2
    )
    $length1 = $ary1.Count;
    $length2 = $ary2.Count;
    $res = @{};
    if($length1 -ne $length2){
        Write-Warning "两个数组的长度必须一致"
    }elseif ($length1 -eq 0) {
        return $res;
    }else{
        for ($i = 0; $i -lt $length1; $i++) {
            $key = $ary1[$i];
            $res.$key = $ary2[$i];
        }
        return $res;
    }
}

调用方法如:

$x=@('a','b','c');
$y = @(1,2,3);
zip -ary1 $x -ary2 $y;

Out[]:

Name                           Value
----                           -----
c                              3
a                              1
b                              2
目录
相关文章
|
3月前
|
移动开发 前端开发 HTML5
Twaver-HTML5基础学习(20)数据容器(3)_数据的批量加载(节省性能方法)
本文介绍了Twaver HTML5中数据的批量加载方法,通过使用`box.startBatch()`可以在大量数据加载时提高性能。文章通过示例代码展示了如何在React组件中使用批量加载功能,以减少界面重绘次数并提升效率。
62 2
Twaver-HTML5基础学习(20)数据容器(3)_数据的批量加载(节省性能方法)
|
3月前
|
XML 存储 JSON
Twaver-HTML5基础学习(19)数据容器(2)_数据序列化_XML、Json
本文介绍了Twaver HTML5中的数据序列化,包括XML和JSON格式的序列化与反序列化方法。文章通过示例代码展示了如何将DataBox中的数据序列化为XML和JSON字符串,以及如何从这些字符串中反序列化数据,重建DataBox中的对象。此外,还提到了用户自定义属性的序列化注册方法。
50 1
|
3月前
|
XML 移动开发 JSON
Twaver-HTML5基础学习(18)数据容器(1)_增删查改、遍历数据容器、包含网元判断
本文介绍了Twaver HTML5中的数据容器(DataBox),包括如何进行增删查改操作、遍历数据容器以及判断网元是否存在于数据容器中。DataBox用于管理所有的网元对象,如ElementBox、LayerBox、AlarmBox等,并通过示例代码展示了其常用方法的使用。
49 1
Twaver-HTML5基础学习(18)数据容器(1)_增删查改、遍历数据容器、包含网元判断
|
3月前
|
存储 索引 Python
python中的数据容器
python中的数据容器
|
3月前
|
存储 安全 Java
ArrayList的基本操作【集合容器知识回顾 ②】
这篇文章详细介绍了ArrayList的基本操作,包括创建对象、添加和删除元素、获取和更新元素、遍历、判断元素存在性、集合的空值检查、批量操作、转换为数组、截取子集合、查找元素索引、克隆拷贝、清空集合以及容量管理等,同时指出了使用ArrayList时的注意事项,如线程安全性、容量管理、删除元素的性能、遍历时的修改、空值处理和性能优化。
ArrayList的基本操作【集合容器知识回顾 ②】
|
4月前
|
安全 网络安全 数据安全/隐私保护
云原生技术探索:容器化与微服务架构的实践之路网络安全与信息安全:保护数据的关键策略
【8月更文挑战第28天】本文将深入探讨云原生技术的核心概念,包括容器化和微服务架构。我们将通过实际案例和代码示例,展示如何在云平台上实现高效的应用部署和管理。文章不仅提供理论知识,还包含实操指南,帮助开发者理解并应用这些前沿技术。 【8月更文挑战第28天】在数字化时代,网络安全和信息安全是保护个人和企业数据的前线防御。本文将探讨网络安全漏洞的成因、加密技术的应用以及提升安全意识的重要性。文章旨在通过分析网络安全的薄弱环节,介绍如何利用加密技术和提高用户警觉性来构建更为坚固的数据保护屏障。
|
4月前
【Application Insights】使用Powershell命令向Application Insgihts发送测试数据
【Application Insights】使用Powershell命令向Application Insgihts发送测试数据
|
4月前
|
监控 安全 网络安全
|
12天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
142 77
|
21天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
下一篇
DataWorks