microsoft-visualbasic-runtime/src/Extensions/Collection/Vector.vb

643 lines
23 KiB
VB.net
Raw Normal View History

2021-01-12 20:24:38 +08:00
#Region "Microsoft.VisualBasic::d11c1fee9f48da20a6ce6a913714ff23, Microsoft.VisualBasic.Core\src\Extensions\Collection\Vector.vb"
2018-08-02 20:14:48 +08:00
' Author:
'
' asuka (amethyst.asuka@gcmodeller.org)
' xie (genetics@smrucc.org)
' xieguigang (xie.guigang@live.com)
'
' Copyright (c) 2018 GPL3 Licensed
'
'
' GNU GENERAL PUBLIC LICENSE (GPL3)
'
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
' GNU General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.
' /********************************************************************************/
' Summaries:
' Module VectorExtensions
'
2019-03-07 22:38:12 +08:00
' Function: (+2 Overloads) After, Append, Coalesce, (+3 Overloads) Delete, (+2 Overloads) Fill
2018-08-02 20:14:48 +08:00
' GetRange, IndexOf, Last, LoadAsNumericVector, MappingData
2021-01-12 20:24:38 +08:00
' Midv, PadLeft, RepeatCalls, Replicate, SetValue
' (+3 Overloads) Sort, Split, VectorShadows
2018-08-02 20:14:48 +08:00
'
' Sub: (+4 Overloads) Add, InsertAt, (+2 Overloads) Memset
'
' /********************************************************************************/
#End Region
Imports System.Runtime.CompilerServices
Imports System.Threading
Imports Microsoft.VisualBasic.ComponentModel
Imports Microsoft.VisualBasic.ComponentModel.Collection
Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
Imports Microsoft.VisualBasic.Language
Imports Microsoft.VisualBasic.Language.Default
Imports Microsoft.VisualBasic.Language.Vectorization
Imports Microsoft.VisualBasic.Linq
Imports Microsoft.VisualBasic.Linq.Extensions
Imports Microsoft.VisualBasic.Linq.IteratorExtensions
2019-08-10 01:31:53 +08:00
Imports Microsoft.VisualBasic.My.JavaScript.Linq
2018-08-02 20:14:48 +08:00
''' <summary>
''' Extension methods for the .NET object sequence
''' </summary>
2019-10-12 22:38:14 +08:00
'''
<HideModuleName>
2018-08-02 20:14:48 +08:00
Public Module VectorExtensions
2021-01-11 20:40:21 +08:00
<Extension>
Public Function PadLeft(Of T)(seq As T(), item As T, width As Integer) As T()
If seq.Length >= width Then
Return seq
Else
Dim d = width - seq.Length
Dim copy As T() = New T(width - 1) {}
For i As Integer = 0 To d - 1
copy(i) = item
Next
For j As Integer = d To width - 1
copy(j) = seq(j - d)
Next
Return copy
End If
End Function
2020-04-26 20:43:13 +08:00
<Extension>
Public Function SetValue(a As Array, value As Object, i As SeqValue(Of Integer)) As Array
Call a.SetValue(value, i.i)
Return a
End Function
2019-10-12 22:38:14 +08:00
''' <summary>
'''
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="a"></param>
''' <param name="compares"></param>
''' <param name="modification">是否修改原始输入的<paramref name="a"/>序列? 否则会创建一个新的数组序列返回</param>
''' <returns></returns>
2019-08-10 01:31:53 +08:00
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function Sort(Of T)(ByRef a As T(), compares As Comparison(Of T), Optional modification As Boolean = True) As T()
If modification Then
Call Array.Sort(a, compares)
Else
2019-10-12 22:38:14 +08:00
Return a.AsEnumerable _
.sort(compares) _
2019-10-12 22:38:14 +08:00
.ToArray
2019-08-10 01:31:53 +08:00
End If
Return a
End Function
2019-01-18 22:42:54 +08:00
''' <summary>
''' 使用<paramref name="template"/>产生一个<paramref name="n"/>长度元素的目标序列
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="template"></param>
''' <param name="n"></param>
''' <returns></returns>
2018-08-02 20:14:48 +08:00
<Extension>
Public Iterator Function Replicate(Of T)(template As T, n%) As IEnumerable(Of T)
For i As Integer = 0 To n - 1
Yield template
Next
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function RepeatCalls(Of T)(factory As Func(Of T), n%, Optional sleep% = 0) As T()
Return n _
.SeqIterator _
.Select(Function(i)
If sleep > 0 Then
Call Thread.Sleep(sleep)
End If
Return factory()
End Function) _
.ToArray
End Function
''' <summary>
2019-10-12 22:38:14 +08:00
''' Dynamics add a element into the target array.(注意:不推荐使用这个函数来频繁的向数组中添加元素,这个函数会频繁的分配内存,效率非常低)
2018-08-02 20:14:48 +08:00
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="value"></param>
2019-10-12 22:38:14 +08:00
<Extension>
Public Sub Add(Of T)(ByRef vector As T(), value As T)
2018-08-02 20:14:48 +08:00
If vector.IsNullOrEmpty Then
vector = {value}
Else
Dim appendBuffer As T() = New T(vector.Length) {}
Call Array.ConstrainedCopy(vector, Scan0, appendBuffer, Scan0, vector.Length)
appendBuffer(vector.Length) = value
vector = appendBuffer
End If
End Sub
''' <summary>
''' Append value collection to the end of the target <paramref name="vector"/>
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="values"></param>
<Extension> Public Sub Add(Of T)(ByRef vector As T(), values As IEnumerable(Of T))
Dim data = values.SafeQuery.ToArray
Dim appendBuffer As T() = New T(vector.Length + data.Length - 1) {}
With vector
Call Array.ConstrainedCopy(
vector, Scan0, appendBuffer, Scan0, .Length)
For Each x As SeqValue(Of T) In data.SeqIterator
appendBuffer(.Length + x.i) = x.value
Next
End With
vector = appendBuffer
End Sub
''' <summary>
''' Add given elements into an array object.(会自动跳过空集合,这个方法是安全的)
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="value"></param>
<Extension> Public Sub Add(Of T)(ByRef vector As T(), ParamArray value As T())
If value.IsNullOrEmpty Then
Return
End If
If vector Is Nothing Then
vector = New T() {}
End If
Dim chunkBuffer As T() = New T(vector.Length + value.Length - 1) {}
Call Array.ConstrainedCopy(vector, Scan0, chunkBuffer, Scan0, vector.Length)
Call Array.ConstrainedCopy(value, Scan0, chunkBuffer, vector.Length, value.Length)
vector = chunkBuffer
End Sub
''' <summary>
''' Add given elements into an array object and then returns the target array object <paramref name="buffer"/>.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="buffer"></param>
''' <param name="value"></param>
''' <returns></returns>
<Extension> Public Function Append(Of T)(buffer As T(), value As IEnumerable(Of T)) As T()
If buffer Is Nothing Then
Return value.ToArray
End If
Call buffer.Add(value.ToArray)
Return buffer
End Function
''' <summary>
''' Add given elements into an array object.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="array"></param>
''' <param name="value"></param>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension> Public Sub Add(Of T)(ByRef array As T(), value As List(Of T))
Call Add(Of T)(array, value.ToArray)
End Sub
2019-01-18 22:42:54 +08:00
''' <summary>
''' 在原来的数组末尾追加由<paramref name="count"/>个<paramref name="item"/>填充的新数据
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="item"></param>
''' <param name="count%"></param>
''' <returns></returns>
2018-08-02 20:14:48 +08:00
<Extension>
Public Function Fill(Of T)(vector As T(), item As T, count%) As T()
If count <= 0 Then
' should returns a copy
Return vector.ToArray
Else
Dim newVector As T() = New T(vector.Length + count - 1) {}
Call Array.ConstrainedCopy(vector, Scan0, newVector, Scan0, vector.Length)
For i As Integer = vector.Length To newVector.Length - 1
newVector(i) = item
Next
Return newVector
End If
End Function
2019-01-18 22:42:54 +08:00
''' <summary>
''' 使用给定的数据序列<paramref name="data"/>填充<paramref name="vector"/>的指定位置开始的区域
''' 填充的长度为<paramref name="data"/>的序列长度
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="data"></param>
''' <param name="start%"></param>
''' <returns></returns>
<Extension>
Public Function Fill(Of T)(ByRef vector As T(),
data As IEnumerable(Of T),
2019-09-04 20:38:12 +08:00
start As i32,
2019-01-18 22:42:54 +08:00
Optional reverse As Boolean = False) As T()
If start < 0 Then
start = vector.Length + start.Value
End If
If reverse Then
For Each x As T In data.Reverse
vector(++start) = x
Next
Else
For Each x As T In data
vector(++start) = x
Next
End If
Return vector
End Function
2018-08-02 20:14:48 +08:00
''' <summary>
2019-06-14 23:49:46 +08:00
''' Removes array element at index.(请注意,这个函数并不会修改原来的数组,而是创建一个新的拷贝)
2018-08-02 20:14:48 +08:00
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="index%"></param>
''' <returns></returns>
<Extension>
Public Function Delete(Of T)(vector As T(), index%) As T()
Dim newVector As T() = New T(vector.Length - 2) {}
Call Array.ConstrainedCopy(vector, Scan0, newVector, Scan0, index)
Call Array.ConstrainedCopy(vector, index + 1, newVector, index, newVector.Length - index)
Return newVector
End Function
''' <summary>
''' 将指定下标的元素从原始的输入序列之中删除然后返回所得到的新序列
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector">原始序列不会被改变</param>
''' <param name="index"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function Delete(Of T)(vector As IEnumerable(Of T), index As IEnumerable(Of Integer)) As T()
Return vector.Takes(index.ToArray,, reversed:=True)
End Function
2019-02-25 19:48:27 +08:00
<Extension>
Public Function Delete(Of T)(list As System.Collections.Generic.List(Of T), index%) As T()
With New System.Collections.Generic.List(Of T)(list)
Call .RemoveAt(index)
Return .ToArray
End With
End Function
2018-08-02 20:14:48 +08:00
<Extension>
Public Sub InsertAt(Of T)(ByRef vector As T(), value As T, index%)
Dim newVector As T() = New T(vector.Length) {}
Call Array.ConstrainedCopy(vector, Scan0, newVector, Scan0, index)
Call Array.ConstrainedCopy(vector, index, newVector, index + 1, vector.Length - index)
vector = newVector
vector(index) = value
End Sub
''' <summary>
''' Create a vector shadow of your data collection.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <returns>返回<see cref="Object"/>类型是为了简化语法</returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function VectorShadows(Of T)(source As IEnumerable(Of T)) As Object
Return New VectorShadows(Of T)(source)
End Function
''' <summary>
''' 聚合将nullable类型结构体转换为原来的值类型
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <returns></returns>
<Extension>
Public Function Coalesce(Of T As Structure)(source As IEnumerable(Of T?)) As IEnumerable(Of T)
Debug.Assert(source IsNot Nothing)
Return source.Where(Function(x) x.HasValue).[Select](Function(x) CType(x, T))
End Function
<Extension>
Public Function Sort(Of T)(source As IEnumerable(Of NamedValue(Of T)), by As Index(Of String), Optional throwNoOrder As Boolean = False) As NamedValue(Of T)()
Dim out As NamedValue(Of T)() = New NamedValue(Of T)(by.Count - 1) {}
For Each x In source
Dim i% = by(x.Name)
If i = -1 Then
If throwNoOrder Then
Throw New InvalidExpressionException(x.Name & " was not found in the index value.")
Else
Continue For
End If
Else
out(i) = x
End If
Next
Return out
End Function
2019-01-18 22:42:54 +08:00
''' <summary>
''' 从所给定的数组之中获取得到指定位置范围内的数据
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="vector"></param>
''' <param name="index%"></param>
''' <param name="count%"></param>
''' <returns></returns>
<Extension> Public Iterator Function GetRange(Of T)(vector As T(), index%, count%) As IEnumerable(Of T)
2018-08-02 20:14:48 +08:00
Dim ends% = index + count - 1
For i As Integer = index To ends
2019-01-18 22:42:54 +08:00
Yield vector(i)
2018-08-02 20:14:48 +08:00
Next
End Function
''' <summary>
''' 对目标序列进行排序生成新的序列,这个拓展函数尝试将升序排序和降序排序这两种操作的接口统一起来
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <param name="desc"></param>
''' <returns></returns>
<Extension>
Public Function Sort(Of T)(source As IEnumerable(Of T), Optional by As Func(Of T, IComparable) = Nothing, Optional desc As Boolean = False) As IEnumerable(Of T)
If by Is Nothing Then
If Not desc Then
Return From x In source Select x Order By x Ascending
Else
Return From x In source Select x Order By x Descending
End If
Else
If Not desc Then
Return source.OrderBy(by)
Else
Return source.OrderByDescending(by)
End If
End If
End Function
Const DimNotAgree$ = "Both a and b their length should be equals or one of them should be length=1!"
''' <summary>
''' 用来生成map数据的
''' + 当两个向量长度相同,会不进行任何处理,即两个向量之间,元素都可以一一对应,
''' + 但是当某一个向量的长度为1的时候就会将该向量补齐因为此时会是一对多的关系
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="a"></param>
''' <param name="b"></param>
''' <returns></returns>
<Extension>
Public Iterator Function MappingData(Of T)(a As T(), b As T()) As IEnumerable(Of Map(Of T, T))
If a.Length = 1 AndAlso b.Length > 1 Then
' 补齐a
a = a(0).Repeats(b.Length)
ElseIf a.Length > 1 AndAlso b.Length = 1 Then
' 补齐b
b = b(0).Repeats(a.Length)
ElseIf a.Length <> b.Length Then
' 无法计算
Throw New ArgumentException(DimNotAgree)
End If
For i As Integer = 0 To a.Length - 1
Yield New Map(Of T, T) With {
.Key = a(i),
.Maps = b(i)
}
Next
End Function
''' <summary>
''' 在一个一维数组中搜索指定对象,并返回其首个匹配项的索引。
''' </summary>
''' <typeparam name="T">数组元素的类型。</typeparam>
''' <param name="array">要搜索的从零开始的一维数组。</param>
''' <param name="o">要在 array 中查找的对象。</param>
''' <returns>如果在整个 array 中找到 value 的第一个匹配项,则为该项的从零开始的索引;否则为 -1。</returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function IndexOf(Of T)(array As T(), o As T) As Integer
2019-04-23 20:31:20 +08:00
If array.IsNullOrEmpty Then
Return -1
Else
Return System.Array.IndexOf(array, value:=o)
End If
2018-08-02 20:14:48 +08:00
End Function
''' <summary>
''' 从后往前访问集合之中的元素请注意请不要使用Linq查询表达式尽量使用``list``或者``array``
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source">请不要使用Linq查询表达式尽量使用``list``或者``array``</param>
''' <param name="index"></param>
''' <returns></returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function Last(Of T)(source As IEnumerable(Of T), index As Integer) As T
Return source(source.Count - index)
End Function
''' <summary>
''' 取出在x元素之后的所有元素
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <param name="x"></param>
''' <returns></returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function After(Of T)(source As IEnumerable(Of T), x As T) As IEnumerable(Of T)
Return source.After(Function(o) x.Equals(o))
End Function
''' <summary>
''' Returns all of the elements which is after the element that detected by a specific
''' evaluation function <paramref name="predicate"/>.
''' (取出在判定条件成立的元素之后的所有元素)
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <param name="predicate"></param>
''' <returns></returns>
<Extension>
Public Iterator Function After(Of T)(source As IEnumerable(Of T), predicate As Predicate(Of T)) As IEnumerable(Of T)
Dim isAfter As Boolean = False
For Each x As T In source
If isAfter Then
Yield x
Else
If predicate(x) Then
isAfter = True
End If
End If
Next
End Function
''' <summary>
''' Replace target array data by using specific object value.(替换目标向量为指定的对象的填充数据)
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="array"></param>
''' <param name="o"></param>
''' <param name="len"></param>
<Extension>
Public Sub Memset(Of T)(ByRef array As T(), o As T, len As Integer)
If array Is Nothing OrElse array.Length < len Then
array = New T(len - 1) {}
End If
For i As Integer = 0 To len - 1
array(i) = o
Next
End Sub
''' <summary>
''' 替换<paramref name="s"/>字符串变量数据为新的字符填充数据
''' </summary>
''' <param name="s"></param>
''' <param name="c"></param>
''' <param name="len"></param>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Sub Memset(ByRef s As String, c As Char, len As Integer)
s = New String(c, len)
End Sub
''' <summary>
''' <see cref="Strings.Mid"/> function like operation on any type collection data.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <param name="start">0 base</param>
''' <param name="length"></param>
''' <returns></returns>
<Extension> Public Function Midv(Of T)(source As IEnumerable(Of T), start%, length%) As T()
If source Is Nothing Then
Return New T() {}
ElseIf source.Count < length Then
Return source.ToArray
End If
Dim array As T() = source.ToArray
Dim ends As Integer = start + length
If ends > array.Length Then
length -= array.Length - ends
End If
Dim buf As T() = New T(length - 1) {}
Call System.Array.ConstrainedCopy(array, start, buf, Scan0, buf.Length)
Return buf
End Function
''' <summary>
''' Load the text file as a numeric vector. Each line in the text file
''' should be a <see cref="Double"/> type numeric value.
''' </summary>
''' <param name="path"></param>
''' <returns></returns>
<Extension> Public Function LoadAsNumericVector(path As String) As Double()
Dim array As String() = IO.File.ReadAllLines(path)
Dim n As Double() = array.Select(AddressOf Val).ToArray
Return n
End Function
''' <summary>
''' Split the object array using a specific evaluation function.
''' (Please note that, all of the object in the <paramref name="source"/> array
''' that match the <paramref name="delimiter"/> evaluation, will not includes
''' in the returned tokens.)
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="source"></param>
''' <param name="delimiter">和字符串的Split函数一样这里作为delimiter的元素都不会出现在结果之中</param>
''' <param name="deliPosition">是否还应该在分区的结果之中包含有分隔符对象?默认不包含</param>
''' <returns></returns>
2020-02-04 23:00:53 +08:00
<Extension>
Public Iterator Function Split(Of T)(source As IEnumerable(Of T), delimiter As Predicate(Of T), Optional deliPosition As DelimiterLocation = DelimiterLocation.NotIncludes) As IEnumerable(Of T())
2018-08-02 20:14:48 +08:00
Dim tmp As New List(Of T)
For Each x As T In source.SafeQuery
' 当前的x元素是分隔符对象
If delimiter(x) = True Then
' 是否将这个分隔符也包含在分组内
' 如果是,则包含在下一个分组内
If deliPosition <> DelimiterLocation.NotIncludes Then
If deliPosition = DelimiterLocation.NextFirst Then
Yield tmp.ToArray
Call tmp.Clear()
Call tmp.Add(x)
Else
' 包含在上一个分块的末尾
Call tmp.Add(x)
Yield tmp.ToArray
tmp *= 0
End If
Else
Yield tmp.ToArray
tmp *= 0
End If
Else
Call tmp.Add(x)
End If
Next
If Not tmp = 0 Then
Yield tmp.ToArray
End If
End Function
End Module