数学基础
- 线性代数
- 概率论与随机过程
- 数值计算
- 蒙特卡洛方法与 MCMC 采样
- 机器学习方法概论
统计学习
深度学习
- 深度学习简介
- 深度前馈网络
- 反向传播算法
- 正则化
- 深度学习中的最优化问题
- 卷积神经网络
- CNN:图像分类
- 循环神经网络 RNN
- Transformer
- 一、Transformer [2017]
- 二、Universal Transformer [2018]
- 三、Transformer-XL [2019]
- 四、GPT1 [2018]
- 五、GPT2 [2019]
- 六、GPT3 [2020]
- 七、OPT [2022]
- 八、BERT [2018]
- 九、XLNet [2019]
- 十、RoBERTa [2019]
- 十一、ERNIE 1.0 [2019]
- 十二、ERNIE 2.0 [2019]
- 十三、ERNIE 3.0 [2021]
- 十四、ERNIE-Huawei [2019]
- 十五、MT-DNN [2019]
- 十六、BART [2019]
- 十七、mBART [2020]
- 十八、SpanBERT [2019]
- 十九、ALBERT [2019]
- 二十、UniLM [2019]
- 二十一、MASS [2019]
- 二十二、MacBERT [2019]
- 二十三、Fine-Tuning Language Models from Human Preferences [2019]
- 二十四 Learning to summarize from human feedback [2020]
- 二十五、InstructGPT [2022]
- 二十六、T5 [2020]
- 二十七、mT5 [2020]
- 二十八、ExT5 [2021]
- 二十九、Muppet [2021]
- 三十、Self-Attention with Relative Position Representations [2018]
- 三十一、USE [2018]
- 三十二、Sentence-BERT [2019]
- 三十三、SimCSE [2021]
- 三十四、BERT-Flow [2020]
- 三十五、BERT-Whitening [2021]
- 三十六、Comparing the Geometry of BERT, ELMo, and GPT-2 Embeddings [2019]
- 三十七、CERT [2020]
- 三十八、DeCLUTR [2020]
- 三十九、CLEAR [2020]
- 四十、ConSERT [2021]
- 四十一、Sentence-T5 [2021]
- 四十二、ULMFiT [2018]
- 四十三、Scaling Laws for Neural Language Models [2020]
- 四十四、Chinchilla [2022]
- 四十七、GLM-130B [2022]
- 四十八、GPT-NeoX-20B [2022]
- 四十九、Bloom [2022]
- 五十、PaLM [2022] (粗读)
- 五十一、PaLM2 [2023](粗读)
- 五十二、Self-Instruct [2022]
- 句子向量
- 词向量
- 传统CTR 预估模型
- CTR 预估模型
- 一、DSSM [2013]
- 二、FNN [2016]
- 三、PNN [2016]
- 四、DeepCrossing [2016]
- 五、Wide 和 Deep [2016]
- 六、DCN [2017]
- 七、DeepFM [2017]
- 八、NFM [2017]
- 九、AFM [2017]
- 十、xDeepFM [2018]
- 十一、ESMM [2018]
- 十二、DIN [2017]
- 十三、DIEN [2019]
- 十四、DSIN [2019]
- 十五、DICM [2017]
- 十六、DeepMCP [2019]
- 十七、MIMN [2019]
- 十八、DMR [2020]
- 十九、MiNet [2020]
- 二十、DSTN [2019]
- 二十一、BST [2019]
- 二十二、SIM [2020]
- 二十三、ESM2 [2019]
- 二十四、MV-DNN [2015]
- 二十五、CAN [2020]
- 二十六、AutoInt [2018]
- 二十七、Fi-GNN [2019]
- 二十八、FwFM [2018]
- 二十九、FM2 [2021]
- 三十、FiBiNET [2019]
- 三十一、AutoFIS [2020]
- 三十三、AFN [2020]
- 三十四、FGCNN [2019]
- 三十五、AutoCross [2019]
- 三十六、InterHAt [2020]
- 三十七、xDeepInt [2023]
- 三十九、AutoDis [2021]
- 四十、MDE [2020]
- 四十一、NIS [2020]
- 四十二、AutoEmb [2020]
- 四十三、AutoDim [2021]
- 四十四、PEP [2021]
- 四十五、DeepLight [2021]
- 图的表达
- 一、DeepWalk [2014]
- 二、LINE [2015]
- 三、GraRep [2015]
- 四、TADW [2015]
- 五、DNGR [2016]
- 六、Node2Vec [2016]
- 七、WALKLETS [2016]
- 八、SDNE [2016]
- 九、CANE [2017]
- 十、EOE [2017]
- 十一、metapath2vec [2017]
- 十二、GraphGAN [2018]
- 十三、struc2vec [2017]
- 十四、GraphWave [2018]
- 十五、NetMF [2017]
- 十六、NetSMF [2019]
- 十七、PTE [2015]
- 十八、HNE [2015]
- 十九、AANE [2017]
- 二十、LANE [2017]
- 二十一、MVE [2017]
- 二十二、PMNE [2017]
- 二十三、ANRL [2018]
- 二十四、DANE [2018]
- 二十五、HERec [2018]
- 二十六、GATNE [2019]
- 二十七、MNE [2018]
- 二十八、MVN2VEC [2018]
- 二十九、SNE [2018]
- 三十、ProNE [2019]
- Graph Embedding 综述
- 图神经网络
- 一、GNN [2009]
- 二、Spectral Networks 和 Deep Locally Connected Networks [2013]
- 三、Fast Localized Spectral Filtering On Graph [2016]
- 四、GCN [2016]
- 五、神经图指纹 [2015]
- 六、GGS-NN [2016]
- 七、PATCHY-SAN [2016]
- 八、GraphSAGE [2017]
- 九、GAT [2017]
- 十、R-GCN [2017]
- 十一、 AGCN [2018]
- 十二、FastGCN [2018]
- 十三、PinSage [2018]
- 十四、GCMC [2017]
- 十五、JK-Net [2018]
- 十六、PPNP [2018]
- 十七、VRGCN [2017]
- 十八、ClusterGCN [2019]
- 十九、LDS-GNN [2019]
- 二十、DIAL-GNN [2019]
- 二十一、HAN [2019]
- 二十二、HetGNN [2019]
- 二十三、HGT [2020]
- 二十四、GPT-GNN [2020]
- 二十五、Geom-GCN [2020]
- 二十六、Graph Network [2018]
- 二十七、GIN [2019]
- 二十八、MPNN [2017]
- 二十九、UniMP [2020]
- 三十、Correct and Smooth [2020]
- 三十一、LGCN [2018]
- 三十二、DGCNN [2018]
- 三十三、AS-GCN
- 三十四、DGI [2018]
- 三十五、DIFFPOLL [2018]
- 三十六、DCNN [2016]
- 三十七、IN [2016]
- 图神经网络 2
- 图神经网络 3
- 推荐算法(传统方法)
- 一、Tapestry [1992]
- 二、GroupLens [1994]
- 三、ItemBased CF [2001]
- 四、Amazon I-2-I CF [2003]
- 五、Slope One Rating-Based CF [2005]
- 六、Bipartite Network Projection [2007]
- 七、Implicit Feedback CF [2008]
- 八、PMF [2008]
- 九、SVD++ [2008]
- 十、MMMF 扩展 [2008]
- 十一、OCCF [2008]
- 十二、BPR [2009]
- 十三、MF for RS [2009]
- 十四、 Netflix BellKor Solution [2009]
- 推荐算法(神经网络方法 1)
- 一、MIND [2019](用于召回)
- 二、DNN For YouTube [2016]
- 三、Recommending What Video to Watch Next [2019]
- 四、ESAM [2020]
- 五、Facebook Embedding Based Retrieval [2020](用于检索)
- 六、Airbnb Search Ranking [2018]
- 七、MOBIUS [2019](用于召回)
- 八、TDM [2018](用于检索)
- 九、DR [2020](用于检索)
- 十、JTM [2019](用于检索)
- 十一、Pinterest Recommender System [2017]
- 十二、DLRM [2019]
- 十三、Applying Deep Learning To Airbnb Search [2018]
- 十四、Improving Deep Learning For Airbnb Search [2020]
- 十五、HOP-Rec [2018]
- 十六、NCF [2017]
- 十七、NGCF [2019]
- 十八、LightGCN [2020]
- 十九、Sampling-Bias-Corrected Neural Modeling [2019](检索)
- 二十、EGES [2018](Matching 阶段)
- 二十一、SDM [2019](Matching 阶段)
- 二十二、COLD [2020 ] (Pre-Ranking 模型)
- 二十三、ComiRec [2020](https://www.wenjiangs.com/doc/0b4e1736-ac78)
- 二十四、EdgeRec [2020]
- 二十五、DPSR [2020](检索)
- 二十六、PDN [2021](mathcing)
- 二十七、时空周期兴趣学习网络ST-PIL [2021]
- 推荐算法之序列推荐
- 一、FPMC [2010]
- 二、GRU4Rec [2015]
- 三、HRM [2015]
- 四、DREAM [2016]
- 五、Improved GRU4Rec [2016]
- 六、NARM [2017]
- 七、HRNN [2017]
- 八、RRN [2017]
- 九、Caser [2018]
- 十、p-RNN [2016]
- 十一、GRU4Rec Top-k Gains [2018]
- 十二、SASRec [2018]
- 十三、RUM [2018]
- 十四、SHAN [2018]
- 十五、Phased LSTM [2016]
- 十六、Time-LSTM [2017]
- 十七、STAMP [2018]
- 十八、Latent Cross [2018]
- 十九、CSRM [2019]
- 二十、SR-GNN [2019]
- 二十一、GC-SAN [2019]
- 二十二、BERT4Rec [2019]
- 二十三、MCPRN [2019]
- 二十四、RepeatNet [2019]
- 二十五、LINet(2019)
- 二十六、NextItNet [2019]
- 二十七、GCE-GNN [2020]
- 二十八、LESSR [2020]
- 二十九、HyperRec [2020]
- 三十、DHCN [2021]
- 三十一、TiSASRec [2020]
- 推荐算法(综述)
- 多任务学习
- 系统架构
- 实践方法论
- 深度强化学习 1
- 自动代码生成
工具
- CRF
- lightgbm
- xgboost
- scikit-learn
- spark
- numpy
- matplotlib
- pandas
- huggingface_transformer
- 一、Tokenizer
- 二、Datasets
- 三、Model
- 四、Trainer
- 五、Evaluator
- 六、Pipeline
- 七、Accelerate
- 八、Autoclass
- 九、应用
- 十、Gradio
Scala
- 环境搭建
- 基础知识
- 函数
- 类
- 样例类和模式匹配
- 测试和注解
- 集合 collection(一)
- 集合collection(二)
- 集成 Java
- 并发
五、提取器
目前为止,构造方法模式都和样例类有关。如
Some(x)
是一个合法的模式,因为Some
是一个样例类。有时候我们需要提取模式,但是不希望创建相关的样例类,此时需要用到提取器。提取器是模式匹配的进一步泛化。在
Scala
中,提取器是拥有名字叫unapply
成员方法的对象。这个unapply
方法的目的是跟某个值做匹配,并将其拆解开。通常提取器对象还会定义一个跟
unapply
相对应的apply
方法用于构建值,不过这不是必须的。如下所示为一个用于处理
email
地址的提取器对象:xxxxxxxxxx
object Email{ def apply(user: String, domain: String) = user + "@" + domain // apply 方法不是必须的 def unapply(str: String) :Option[(String,String)] = { val parts = str split "@" if(parts.length == 2) Some(parts(0), partes(1)) else None } }这个对象同时定义了
apply
方法和unapply
方法。apply
方法使得我们可以像调用函数一样调用Email
对象:Email("huaxz","163.com)
。如果想要更明显的表明意图,还可以让
Email
继承自Scala
的函数类型:xxxxxxxxxx
object Email extends ((String,String) => String){ ... }这个对象声明当中的
(String,String) => String
,其含义跟Function2[String, String, String]
一样。unapply
方法就是将Email
变成提取器的核心方法。从某种意义上讲,它是apply
方法的逆运算。但是unapply
还需要处理输入的字符串不是email
的情况,这也是为什么unapply
方法的返回类型为Option
类型。
每当模式匹配遇到引用提取器对象的模式时,它都会调用提取器的
unapply
方法。例如:xxxxxxxxxx
str match{ case Email(user, domain) => ... case ... }这将会引发如下调用
Email.unapply(str)
。调用结果要么返回None
,要么返回Some(u,d)
。- 如果返回
Some(u,d)
,则模式能够匹配,则变量user
会被绑到返回值u
,变量domain
会绑到返回值d
。 - 如果返回
None
,则模式未能匹配。系统会继续尝试匹配下一个模式。如果没有下一个模式,则系统返回MatchError
异常。
另外,这里的
str
的类型可以是Email.unapply
的参数类型,但也可以不是。我们可以用提取器来匹配更笼统的类型表达式:xxxxxxxxxx
val x: Any = .... x match{ case Email(user, domain) => ... case ... }此时:
- 系统首先会检查给定的值
x
是否满足Email.unapply
参数类型的要求。如果满足要求,则x
就会被转成String
(Email.unapply
的参数类型),然后继续上述讨论的模式匹配流程。 - 如果类型不满足要求,则模式未能匹配,系统继续尝试匹配下一个模式。
- 如果返回
在对象
Email
中,apply
方法称作注入injection
,因为它接收某些入参,并交出给定的集合(在我们这里表示能够代表email
地址的字符串集合);unapply
方法称作提取extraction
,因为它接收上述集合的一个元素,并将它的某些组成部分提取出来。injection
和extraction
通常成对地出现在对象中,因为这样一来我们就可以同时表示构造方法和模式。但是我们也可以在不定义injection
的情况下单独定义extraction
。含有
extraction
的对象被称作提取器extractor
,无论它是否有apply
方法。如果同时包含了
injection
和extraction
方法,则它们是一对dual
对偶方法。虽然这不是scala
语法必须的,但是我们建议这么做。xxxxxxxxxx
Email.unapply(Email.apply(user, domain)) // 应该返回 Some(user,domain)
5.1 提取 0 个或 1个变量的模式
前面的
unapply
方法在成功的时候返回一对元素的值,这很容易推广到多个变量的模式。如果需要绑定N
个变量,则unapply
方法可以返回一个以Some
包起来的N
个元素的元组。但是,当模式只绑定一个变量时,处理逻辑是不同的。
Scala
并没有单个元素的元组,因此unapply
方法只是简单地将元素本身放到Some
里。如以下提取器提取连续相同的子串:
xxxxxxxxxx
object Twice{ def apply(s: String): String = s + s def unapply(s: String): Option[String] = { val len= s.length / 2 val halfStr = s.substring(0, len) if(halfStr == s.substring(len)) Some(halfStr) else None } }也可能某个提取器模式不绑定任何变量,这时对应的
unapply
方法返回布尔值(true
表示成功,false
表示失败)。xxxxxxxxxx
object UpperCase{ def unapply(s: String): Boolean = s.toUpperCase == s }这里仅定义了
unapply
,并没有定义apply
。定义apply
没有任何意义,因为没有任何东西需要构造。
如果我们希望匹配那些大写的、重复出现的子串。因此可以这样使用提取器:
xxxxxxxxxx
s math{ case Twice(x @ UpperCase()) => println("match :"+x) }这里有两点注意:
UpperCase()
的空参数列表不能省略,否则匹配的就是和UpperCase
这个对象的相等性。- 尽管
UppserCase()
本身不绑定任何变量,但是我们仍然可以将跟它匹配的整个模式关联一个变量。做法是标准变量绑定机制:x @ UpperCase()
。这样的写法将变量x
跟UpperCase()
匹配的模式关联起来。
5.2 提取可变长度参数的模式
目前的
unapply
方法只支持固定长度的变量匹配。为支持可变长度的变量匹配,scala
允许我们定义另一个不同的 提取方法:unapplySeq
。xxxxxxxxxx
object Domain{ def apply(parts: String*) : String = parts.reverse.mkString(".") def unapplySeq(str: String): Option[Seq[String]] = Some(str.split("\\.").reverse) }这里
unapplySeq
的结果类型必须符合Option[Seq[T]]
的要求,其中元素类型T
可以为任意 类型。另外,这里的
apply
方法也不是必须的。从
unapplySeq
返回“固定元素 + 可变部分” 也是可行的。这是通过将所有元素放在一个元组里面来实现的,其中可变部分放在元组最后:xxxxxxxxxx
object Email{ def unapplySeq(email: String): Option[(String,Seq[String])] ={ val partes = email split "@" if(partes.length == 2) Some(partes(0), partes(1).split("\\.").reverse) else None } }
5.3 提取器和序列模式
可以使用序列模式来访问列表或数组的元素,如:
xxxxxxxxxx
List() List(x, y, _*) Array(x, 0, 0, _)事实上,
Scala
标准库中的这些序列模式都是用提取器实现的。例如List(...)
这样的模式之所以可行,是因为scala.List
的伴生对象是一个定义了unapplySeq
方法的提取器。xxxxxxxxxx
package scala object List{ def apply[T](elems: T*) = elems.toList def unapplySeq[T](x: List[T]): Option[Seq[T]] = Some(x) }List
伴生对象包含了一个接收可变数量的入参的apply
方法,正是这个方法让我们可以编写List(), List(1, 2, 3)
这样的表达式。List
伴生对象还有一个以序列形式返回列表所有元素的unapplySeq
方法。正是这个方法在支撑List(...)
这样的模式。scala.Array
对象中我们也能够找到类似的定义,这些定义支持对数组的injection
和extraction
。
5.4 提取器 vs 样例类
样例类的缺点:样例类将数据的具体表现类型暴露给使用方。这意味着构造方法模式中使用的类名和选择器对象的具体表现类型相关。
例如如下的模式匹配:
xxxxxxxxxx
a match{ case C(...) }如果匹配成功了,那么你就知道选择器表达式是
C
这个类的实例。提取器打破了数据表现和模式之间的关联:提取器支持的模式,和被选择的对象的数据类型没有任何联系。这个性质被称作表现独立
representation independence
。这很重要,因为它允许我们修改某些组件的实现类型,同时又不影响这些组件的使用方。如果你的组件定义了样例类,你就难以修改这些样例类,因为使用方可能已经包含了对这些样例类的模式匹配。重命名这些样例类,或者改变类的继承关系都会影响到使用方的代码。
提取器没有这个问题,因为它们介于数据表现层和使用方看到的内容之间。你可以改变某个类型的具体表现形式,而不影响使用方的代码。
表现独立是提取器对于样例类的一个重要优势。另一方面,样例类也有一些相对于提取器的优势。
- 首先,设置和定义样例类要比提取器简单得多,需要的代码也更少。
- 其次,样例类比提取器能够带来更高效的模式匹配,因为
scala
编译器能够对使用样例类的模式做更好的优化。 - 最后,如果你的样例类继承自一个
sealed
基类,那么scala
编译器将检查你的模式匹配是否完整全面。如果有的值没有被覆盖到,则编译时会报错。对于提取器而言,并不存在这样的全面性检查。
至于选择样例类还是提取器,视具体情况而定。
如果你编写的是一个封闭的应用,则样例类通常更好,因为它们精简、快速、还可以有静态检查。
如果你之后决定需要修改你的类继承关系,则应用需要被重构。
如果你需要将类型暴露给第三方,那么提取器可能是更好的选择,因为它们保持了表现独立。
通常建议从样例类开始,随着需求变化再切换成提取器。
5.5 正则表达式
提取器的一个应用场景是正则表达式。和
Java
一样,scala
也通过类库提供了正则表达式的支持,不过提取器让我们更容易使用正则表达式。Scala
从Java
继承了正则表达式语法,而Java
又从Perl
继承了大部分的正则表达式特性。Scala
的正则表达式类位于scala.util.matching
包scala.util.matching.Regex
。创建一个正则表达式是将一个字符串传给Regex
方法来创建的。如:xxxxxxxxxx
import scala.util.matching.Regex val reg = new Regex("(-)?(\\d+)(\\.\\d*)?")这里
\
出现多次。这是因为在Java
和Scala
中,字符串中的单个反斜杠表示转义字符,而不是字符串中的常规字符。所以你需要写出\\
从而得到字符串里的一个\
字符。如果正则表达式中有很多反斜杠,那么写起来和读起来都比较痛苦。可以使用
Scala
原生字符串来解决这个问题。Scala
原生字符串是由一对三个连续双引号括起来的字符序列,它和普通字符串的区别在于:原生字符串中的字符和它本身一样,没有转义。因此上述正则表达式也可以写成:
xxxxxxxxxx
val reg = new Regex("""(-)?(\d+)(\.\d*)?""")在
scala
中一种更短的创建正则表达式的方式是:xxxxxxxxxx
val reg = """(-)?(\d+)(\.\d*)?""".r这是由于
StringOps
类有一个名为.r
的方法,它将字符串转换为正则表达式。其定义为:xxxxxxxxxx
package scala.runtime import scala.util.matching.Regex class StringOps(self: String)...{ ... def r = new Regex(self) }有几种利用正则表达式查找的方法:
regex findFirstIn str
:查找str
中正则表达式首次出现的结果,结果为Option
类型(找不到就是None
)。xxxxxxxxxx
val reg = new Regex("""(-)?(\d+)(\.\d*)?""") reg findFirstIn "123.5 abc 45.6" // 返回 Option[String] = Some(123.5)regex findAllIn str
:查找str
中正则表达式所有出现的结果,结果为Iterator
类型。xxxxxxxxxx
val reg = new Regex("""(-)?(\d+)(\.\d*)?""") reg findFirstIn "123.5 abc 45.6" // 返回一个非空迭代器,迭代内容为 ["123.5", "45.6"]findPrefixOf str
:查找str
以正则表达式开始(即str
的头部)的结果,结果为Option
类型(找不到就是None
)。xxxxxxxxxx
val reg = new Regex("""(-)?(\d+)(\.\d*)?""") reg findFirstIn "de 123.5" // 返回 Option[String] = Some(123.5) reg findPrefixOf "de 123.5" // 返回 None reg findPrefixOf "123.5 de" // 返回 Option[String] = Some(123.5)
在
scala
中每个正则表达式都定义了一个提取器。该提取器用来识别正则表达式中的分组(正则表达式中每个括号()
代表一个分组)匹配的子字符串。例如:
xxxxxxxxxx
val reg = """(-)?(\d+)(\.\d*)?""".r val reg(sign, intPart, deciPart) = "-1.23" // sign 被绑定为 '-', intPart 被绑定为 '1', deciPart 被绑定为 '.23'在这里,模式
reg(...)
被用在了val
的定义中。这里的reg
正则表达式定义了一个unapplySeq
方法,这个方法会匹配任何与reg
匹配的字符串。如果字符串匹配了,那么正则表达式
reg
中的三个分组对应的部分就被作为模式的元素返回,进而被sign, intPart, deciPart
绑定。如果某个分组缺失,则对应的元素值就被设为
null
。如:xxxxxxxxxx
val reg = """(-)?(\d+)(\.\d*)?""".r val reg(sign, intPart, deciPart) = "1.23" // sign 被绑定为 null , intPart 被绑定为 '1', deciPart 被绑定为 '.23'如果无法匹配,则抛出
MatchError
异常。注意:这里要求字符串和正则表达式从头到尾完全匹配。这不同于
findFirstIn/findAllIn/findPrefixOf
,后者只需要字符串中部分匹配即可。
我们也可以在
for
表达式中混用提取器和正则表达式的find
。如下面的示例对字符串中找到的所有十进制数字进行拆解:xxxxxxxxxx
val reg = """(-)?(\d+)(\.\d*)?""".r for (reg(s,i,d) <- reg.findAllIn(inputStr)) println("sign: %s, integer: %s, decimal: %s".format(s, i, d))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论