Jiahong的个人博客

凡事预则立不预则废


  • Home

  • Tags

  • Archives

  • Search

Sklearn——模型方法和参数使用笔记

Posted on 2018-06-25

Sklearn——模型方法和参数使用笔记
本文不定期更新


模型的一般使用流程

  • init
  • fit(x_train, y_train)
  • predict(x_test)

模型输出预测概率

  • 方法名称: predict_proba(x_test)
  • 该方法对于预测时使用概率或者分数的算法来说直接返回概率值,对于不能返回概率的类来说一般返回交叉验证结果的平均值等
    • NB: 概率值
    • LR: 逻辑回归的分数
    • SVM: 交叉验证生成的平均值, 这里的结果与predict预测结果可能有偏差

关于参数

* 

Pandas——为DataFrame的某一列实行OneHot编码

Posted on 2018-06-23

为DataFrame的某一列实行OneHot编码


使用OneHotEncoder进行编码

  • 基本实现思路:

    • 生成一个OneHotEncoder对象
    • 取出对应的列并处理成N*1维的数组,用其训练OneHotEncoder对象并进行编码转换
    • 将新编码的数据生成为新的DataFrame对象
      • 为新的编码每一列生成新的列名称
      • 为新的每行索引赋值为原始DataFrame对应的索引
    • 按照列合并两个DataFrame
    • 删除之前的列
  • 实现代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from sklearn.preprocessing import OneHotEncoder

    def one_hot_for_column(df, column):
    """
    encode column of df with one hot method
    :param df: an object of DataFrame
    :param column: the name of column in df
    :return: new object of DataFrame and object of OneHotEncoder
    """
    ohe = OneHotEncoder()

    # ohe.fit(df[column].values.reshape(-1, 1))
    # col_series = ohe.transform(df[column].values.reshape(-1, 1)).toarray()
    # <==>
    col_series = ohe.fit_transform(df[column].values.reshape(-1, 1)).toarray()

    columns = ["%s_%s" % (column, str(m)) for m in range(1, col_series.shape[1] + 1)]
    sub_df = pd.DataFrame(col_series, columns=columns, dtype=int, index=df.index)
    new_df = pd.concat([df, sub_df], axis=1)
    new_df.drop(columns=column, inplace=True)
    return new_df, ohe

MySQL——Error code 28; No space left on device

Posted on 2018-06-06

首先说明这是一个MySQL错误,描述的是MySQL临时文件不能打开,空间不足


问题源头

在Ubuntu系统中,爬取数据过多,数量百万级到千万级,而且存储都是以小文件的方式存储的,造成inode的大量使用,从而发生异常,发生异常时服务器尚有20多G的硬盘空间


解决方式

将数据打包起来,然后删除打包后的数据


总结

在做数据分析时最好不要使用小文件存储数据,如果非要存储收也尽量注意打包压缩暂时不必要使用的文件

Shadowsocks——混淆设置

Posted on 2018-06-04

obfs: obfuscating

  • Follow Github项目https://github.com/shadowsocks/simple-obfs
  • 可选http和tls两种混淆方式,根据需求修改即可

NLP——词嵌入的前世今生

Posted on 2018-06-01

词嵌入的发展,NNLM,CBOW,skip-gram
词嵌入(Word Embedding)也称为词向量(Word2Vec): 指的是将词转换成向量的形式


基本问题

  • 用语言模型做预训练
    • 语言模型: 如何计算一段文本序列在某种语言下出现的概率?
  • 如何表示文本中的一个词?

文本表示模型

词袋模型

  • One-Hot: 向量维度与词的数量相同,某个维度为1,其他都为0
  • TF-IDF: 计算词对文档的贡献(权重)
  • TextRank: 类似于PageRank的思想
    • 如果一个单词出现在很多单词后面的话,那么说明这个单词比较重要
    • 一个TextRank值很高的单词后面跟着的一个单词,那么这个单词的TextRank值会相应地因此而提高
优缺点
  • OneHot:
    • 维度灾难
    • 语义鸿沟
  • TF-IDF和TextRank
    • [个人理解]也无法表示语义,所以存在语义鸿沟

主题模型

  • LSA: 矩阵分解(SVD)的方式,又名 Latent Semantic Indexing
  • pLSA: 简单的概率隐式语义模型
  • LDA: 无监督聚类常用的模型,有Gibbs Sampling和变分推断等实现,网上有很多资料,也有很多实现
  • L-LDA: LDA的有监督版本,核心思想就是在LDA吉布斯实现的版本基础上加上采样限制,使得每次采样只能控制在对应主题上,效果比较好,网上没有官方的实现,大部分实现不完全,感兴趣可以试试我的实现Labeled-LDA-Python
    • L-LDA理论上可以得到比LDA质量更高,效果更好的词向量
优缺点:
  • LSA:
    • 利用全局语料特征
    • 但SVD求解计算复杂度大

基于词向量的固定表征

  • Word2vec: 基于上下文训练, 拥有相同上下文的词相近,基于分布式假设(相同上下文语境的词有似含义)
  • FastText: 通过对”词-词”共现矩阵进行分解从而得到词表示的方法,基于分布式假设(相同上下文语境的词有似含义)
  • GloVe: 基于全局预料,结合了LSA和word2vec的优点, 论文: GloVe: Global Vectors for Word Representation
优缺点
  • 目前提到的以上所有都是静态词向量,无法解决一次多义等问题
  • Word2vec和FastText:
    • 优化效率高,但是基于局部语料
    • 本质上二者也都是语言模型
  • GloVe:
    • 基于全局语料库、并结合上下文语境构建词向量, 结合了LSA和word2vec的优点
    • 可以看作是更换了目标函数和权重函数的全局word2vec

基于词向量的动态表征

  • ELMo:
    • 采样LSTM提取特征
    • 采用双向语言模型
    • 实际上是两个单向语言模型(方向相反)的拼接,这种融合特征的能力比BERT一体化融合特征方式弱
  • GPT:
    • 采样Transformer进行特征提取
    • 采用单向语言模型
  • BERT:
    • 采用Transformer进行特征提取
    • 采用双向语言模型
优缺点
  • 三者都是基于语言模型的动态词向量,能解决一次多义问题
  • ELMo:
    • 采用1层静态向量+2层LSTM,多层提取能力有限
  • GPT和BERT:
    • Transformer可采用多层,并行计算能力强
  • 很多任务表明:
    • Transformer特征提取能力强于LSTM
  • GPT和BERT都采用Transformer,Transformer是encoder-decoder结构,GPT的单向语言模型采用decoder部分,decoder的部分见到的都是不完整的句子;BERT的双向语言模型则采用encoder部分,采用了完整句子

NNLM/RNNLM

  • 词向量为副产物,存在效率不高等问题;

不同模型的对比

word2vec vs NNLM

  • 本质上都可以看做是语言模型
  • 对NNLM来说,目不是词向量,是语言模型
  • word2vec虽然本质上也是语言模型,但是更关注词向量本身,因此做了很多优化来提高计算效率
    • 与NNLM相比,词向量直接sum(CBOW),而不是拼接,并取消隐藏层
    • 考虑到Softmax需要遍历整个词汇表,采用 Hierarcal Softmax和 Negative Sampling进行优化
    • word2vec所做的一切都是为了一切为了快速生成词向量

word2vec vs fastText

  • 都可以进行无监督学习, fastText训练词向量时会考虑subword(子词)
    • 在fastText中,每个中心词被表示成子词的集合。下面我们用单词“where”作为例子来了解子词是如何产生的。首先,我们在单词的首尾分别添加特殊字符“<”和“>”以区分作为前后缀的子词。然后,将单词当成一个由字符构成的序列来提取n元语法。例如,当n=3时,我们得到所有长度为3的子词:“<wh>” “whe” “her” “ere” “<re>”以及特殊子词“<where>”。
  • fastText还可以进行有监督学习进行文本分类:
    • 结构与CBOW类似,但学习目标是人工标注的分类结果, 而不是中心词 \(w\)
    • 采用hierarchical softmax对输出的分类标签建立哈夫曼树,样本中标签多的类别被分配短的搜寻路径;
    • 引入N-gram,考虑词序特征;
    • 引入subword(子词)来处理长词,处理未登陆词问题

word2vec vs GloVe vs LSA

  • word2vec vs GloVe
    • 语料库:
      • word2vec是局部语料库训练的,特征提取基于滑动窗口,可以在线学习
      • GloVe的滑动窗口是为了建立”词-词”共现矩阵,需要统计共现概率,不能在线学习
    • 无监督学习:
      • word2vec是无监督学习
      • GloVe通常被认为是无监督学习(无需标注),但实际上GloVe还是有label的,即共现次数 \(log(X_{ij})\)
    • 损失函数:
      • word2vec损失函数实质上是带权重的交叉熵,权重固定;
      • GloVe的损失函数是最小平方损失函数,权重可以做映射变换
    • 总结:
      • word2vec的损失函数是带权重的交叉熵, 权重是固定的
      • GloVe可以看作是目标函数为MSE,权重函数可变的全局word2vec
  • GloVe vs LSA
    • LSA(Latent Semantic Analysis)可以基于co-occurance matrix构建词向量,实质上是基于全局语料采用SVD进行矩阵分解,然而SVD计算复杂度高;
    • GloVe可看作是对LSA一种优化的高效矩阵分解算法,采用Adagrad对最小平方损失进行优化;

ELMo、GPT、BERT

  • 三者使用了两种不同的NN组件提取特征
    • ELMo: LSTM
    • GPT, BERT: Transformer
  • 三者使用了两种不同的语言模型:
    • GPT: 单向语言模型
    • ELMo, BERT: 双向语言模型
    • ELMo实际上是两个方向相反的单向语言模型的拼接, 融合特征的能力比BERT那种一体化的融合特征方式弱
    • GPT和BERT都采用Transformer,Transformer是 Encoder-Decoder结构
      • GPT为单向语言模型,采用的是Decoder部分, Decoder部分见到的都是不完整的句子
      • BERT为双向语言模型,采用的是Encoder部分,采用了完整的句子

下面是一些模型的详细介绍


NNLM

模型目标

  • 给定一段长度为m的序列时,预测下一个词(第m+1)个词出现的概率
  • 参考博客DL——NNLM

word2vec

  • 此处只做简单介绍,更详细的讲解请参考博客word2vec学习笔记
  • 损失函数: 带权重的交叉熵损失函数(权重固定)

两类实现

  • 一般实现: 每次训练一个样本 \((w_{in}, w_{out})\), 需要计算所有词和当前词一起对应的 softmax, 很耗时间,需要 \(O(V)\)复杂度的时间
  • 优化实现: Hierarcal Softmax 和 Negative Sampling 两种方式
    • Hierarcal Softmax: 每次训练一个样本 \((w_{in}, w_{out})\), 只需要更新平均 \(O(log V)\) 个非叶子节点(每个非叶子结点就是一个词向量)即可
    • Negative Sampling: 每次训练一个样本 \((w_{in}, w_{out})\), 只需要更新 \({w_j|w_j\in {w_O}\bigcup W_{neg}}\)个词向量, 通常负样本的集合大小取 \(log(V)\) 量级甚至更小
层次Softmax模型框架

层次Softmax Hierarchical Softmax模型

  • 核心思想: 输出层由语料库中词出现的频数当作权值构造出的哈夫曼树作为输出
  • 引入哈夫曼树, Hierarcal Softmax是一种有效计算Softmax的方式,使用二叉树来表示
  • 其中Hierarchical Softmax模型的输出层由语料库中词出现的频数当作权值构造出的哈夫曼树作为输出
  • CBOW
    • 输入层: 2c个词向量
    • 投影层: 2c个词向量的累加
    • 输出层: 哈夫曼树(重点是词w所在的叶子节点, 以及w到根节点的路径)
      • 所有单词没有输出向量表示形式,每个内部结点有一个输出向量 \(v\)
      • 输出层共有 \(V - 1\) 个非叶节点, 也就是要学习 \(V - 1\) 个输出向量
      • 看起来并没有什么优化,但是每次迭代训练一个样本\(w_{in}, w_{out}\) 时,我们只需要优化从根节点到输出单词 \(w_{out}\) 的路径上的输出向量即可, 平均共 \(O(log V)\) 个向量
      • 每个单词的概率计算需要考虑叶子节点来求出[待更新]
  • Skip-gram
    • 输入层:词w的向量
    • 投影层:依旧是词w的向量
    • 输出层:哈夫曼树(重点是词w的上下文窗内2c个词所在的叶子节点,以及各自到根节点的路径)
Negative Sampling模型框架
  • 实际上就是简单的对每一个样本中每一个词都进行负例采样(本质上就是用负采样代替了层次softmax的哈弗曼树)
  • 负采样是噪声对比估计的一个简化版本,目的是提高训练速度并改善所得词向量的质量
  • 核心思想: 在每次迭代过程中, 有大量的输出向量需要更新,为了解决这一困难, Negative Sampling的做法是只更新其中一部分输出向量
  • 利用不同词在语料中出现的频次多少来决定被采样到的概率(单词出现的频次越大,越可能被选做负样本)
  • 负采样: 利用不同词在语料库中出现的频次多少来决定该词被采样到的概率
  • 每次训练一个样本 \((w_{in}, w_{out})\), 只需要更新 \({w_j|w_j\in {w_O}\bigcup W_{neg}}\)个词向量, 通常负样本的集合大小取 \(log(V)\) 量级甚至更小
  • CBOW
    • 输入层: 2c个词向量
    • 投影层:2c个词向量的累加
    • 输出层:负采样词集(重点是词w的负词词集的参数 \(\theta\) ,负词的概率永远是1-Sigmoid函数值)
  • Skip-gram模型:
    • 输入层:词w的向量
    • 投影层:依旧是词w的向量
    • 输出层:每个上下文词u的负采样(重点是词w的负词词集的参数 \(\theta\) ,负词的概率永远是1-Sigmoid函数值)
总结
  • 层次Softmax模型:
    • CBOW模型的一次更新是:
      • 输入2c个词向量的累加(简单的sum,而不是拼接)
      • 对中心词 \(w\) 上的路径节点系数进行更新(输出层)
      • 对所有的上下文词的词向量进行整体一致更新(输入层到隐藏层的参数, 修改多个词向量)
    • Skip-gram模型的一次更新是:
      • 输入中心词 \(w\) 的词向量(直接输入,无其他操作)
      • 对每个上下文词 \(u(i)\) 所在的路径上的节点系数进行更新(输出层,修改多个上下文的结点)
      • 对词w的词向量进行单独更新(输入层到隐藏层的参数,只修改一个词向量)
    • CBOW一次训练更新计算量小,Skip-gram一次训练计算量大(更新的系数更多?)
  • CBOW看起来更新的更平滑,适合小量文本集上的词向量构建,Skip-gram每次更新都更加有针对性,所以对于大文本集上表现更好

word2vec的缺陷

  • 不能解决多义词问题(只能把几种意思同时编码进向量里)
    • 这是所有固定词向量的通病
    • 解决方案是动态词向量(允许不同上下文使用不同词向量表达同一个词),包括ELMo, GPT, BERT

GitHub——主题推荐算法

Posted on 2018-05-30

Topic Suggestions for Millions of Repositories

Github官网原文


总体流程图

分为七个步骤:


Readme 预处理与清除

(Readme preprocessing and cleanup)

  • 移除不要的文本块(Remove unwanted blocks)
    • 去除没用文本:一些块是没用的,比如code, table 和image链接等
  • 文本划分(Text segmentation)
    • 提取有用的文本:一个启发式的README tagger,借助格式分析:分析缩进(indentation),空格(spacing),和反撇号( `, backtick)等决定是否是有用信息【说明:这里语法分析是没必要的,我们只关心有用的文本,其他的都是为噪音(noise)】
    • 提取到有用文本后,删除拓展部分:HTML标签,路径和其他处理出来的部分等
    • 最后将剩下的文本进行粗粒度分割【用标点符号(punctuation marks)和README中的一些符号,比如临近哈希符号(contiguous hash symbols)】

生成候选主题

(Generate candidate topics)

  • 用自定义的停用词去将词单元划分出来(Use custom stop words to break text into word units)
    • 停用词去除
    • n-gram分段小于等于4(测试发现小于4的比较好,太长的主题过于具体了,不合适)

移除噪音主题

(Eliminate noisy topics)

  • 用逻辑回归模型删减“bad”主题(Use a logistic regression model to prune “bad” topics)
    • 监督逻辑回归模型(supervised logistic regression model)主要针对除了频数比较小的外,一些不好的,比如”great”, “cool”等,这里的模型是一个分类模型,分为两类(good[positive] and bad(negative)), 我们称之为关键词过滤模型
      • 手动收集大约300个数据集作为训练集
      • 单个动词一般都是Bad类请教师兄
      • 其他的(Other features we used were occurrence of user names, n-gram size of a phrase, length of a phrase, and numeric content within a phrase.)
      • 以后打算加入回馈机制(来自用户的)去更新这个关键词过滤模型:接受度高的词作为positive的,接受度低的作为停用词或者negative的
  • 移除不满足最小频数的主题(Eliminate topics not satisfying minimum frequency count)

给主题评分

(Score Topics)

  • 用混合tf-idf分数,以主题频率和n元词作为打分标准(Use combination of tf-idf scores, topic frequency and n-gram size for scoring)
    • 评估多种指标后,选择了点互信息指标(PMI): Pointwise Mutual Information
    • 考虑另一种指标tf-idf:参考论文Using TF-IDF to Determine Word Relevance in Document Queries.pdf或者Python的实现
    • The second approach we tried uses the average tf-idf scores of individual words in a phrase weighted by the phrase frequency (if it’s more than one word long) and n-gram size.
    • 两种方法比较:
      • PMI: 强调独特性,越特殊的短语评分越高,但有些可能只是拼写错误(Typo)或者是没有被删除的代码片段
      • tf-idf: 不强调独特性,最终选择是tf-idf,原因是这个指标较好的平衡了独特性和主题与仓库(repository)的相关程度
    • 在tf-idf的基础上还添加了一下其他的比如boosting scores等方法
    • 下面是TF-IDF的说明*

规范化主题

(Canonicalize topics)

  • 使用内部词典规范主题形式(Use an in-house dictionary to suggest canonical form of topics)

    • 解决文字层面的差别和变化等,比如下面四个主题

      neural-network
      neural-networks
      neuralnetwork
      neuralnetworks


移除相似的主题

(Eliminate near similar topics)

  • 用基于Jaccard相似性评分的贪心算法(Greedy approach using Jaccard similarity scoring)

    • motivation:在得到Top-N的主题后,有些主题其实很相似,虽然都有用,但是他们只是在不同粒度描述了同一个主题而已,因此我们需要删除一些重复的,比如下面的例子

      machine learning
      deep learning
      general-purpose machine learning
      machine learning library
      machine learning algorithms
      distributed machine learning
      machine learning framework
      deep learning library
      support vector machine
      linear regression

    • method: 两个短语的相似性计算使用的是基于词的Jaccard相似性(两个短语的差集与并集的比值,因为它对较短的短语很有效,而且分数是[0-1]的,很方便设置阈值(thresholds)),用贪心算法,如果两个主题相似,去除分数较低的那一个,上面的例子去除相似主题后的结果是:

      machine learning
      deep learning
      support vector machine
      linear regression


返回Top-K主题作为最终结果

Go语言——String与Slice深度解析

Posted on 2018-05-29

Note: 他们都是struct类型的


String

type StringHeader struct {
    Data uintptr
    Len  int
}

Slice

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

把Slice转换为String的语法为

[]byte("string")
string([]byte{1,2,3})

注意:这种实现会有拷贝操作


如何避免拷贝操作呢?

答案是自己实现指针转换(也可用反射实现头部转换),省去复制数据部分,同时注意这种实现后底层的数据不能再更改了,不然容易引发错误

直接修改指针类型并构建相应头部

func String2Slice(s string) []byte {
    sp := *(*[2]uintptr)(unsafe.Pointer(&s))
    bp := [3]uintptr{sp[0], sp[1], sp[1]}
    return *(*[]byte)(unsafe.Pointer(&bp))
}

func BytesToString(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}

使用反射机制获取到头部再进行转换

func Slice2String(b []byte) (s string) {
    pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    pstring := (*reflect.StringHeader)(unsafe.Pointer(&s))
    pstring.Data = pbytes.Data
    pstring.Len = pbytes.Len
    return
}


func String2Slice(s string) (b []byte) {
    pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    pstring := (*reflect.StringHeader)(unsafe.Pointer(&s))
    pbytes.Data = pstring.Data
    pbytes.Len = pstring.Len
    pbytes.Cap = pstring.Len
    return
}

MySQL——事务深度理解

Posted on 2018-05-26

@@autocommit

  • 变量 @@autocommit
    select @@autocommit;
    set @@autocommit = 0;
  • @@autocommit 为0时表示不以显示事务开头的语句或者以事务开头(begin; 或者 start transaction;)都会被缓存起来并且在commit;提交前都可以用rollback;回滚
  • @@autocommit 为1时表示必须以事务开头的语句才会被缓存,否则一个sql语句将会被当做一个事务提交,将不能使用rollback;语句回滚

说明:不是所有引擎都支持事务,常用的支持事务的引擎是InnoDB

MySQL——引擎比较说明

Posted on 2018-05-26

关于引擎

  • 查看各种引擎 show engines;
  • 查看当前默认引擎 show variables like '%storage_engine%';
  • 查看指定表的引擎 show create table tableName;
  • 修改指定表的引擎 alter table tableName engine = innodb;
  • 创建表时指定引擎 create table mytable (id int, titlechar(20)) engine = innodb
  • 修改默认存储引擎
    在mysql配置文件(linux下为/etc/my.cnf),在mysqld后面增加default-storage-engine=INNODB即可

不同引擎的事务支持说明

MySQL数据库有多种引擎,一般使用的是InnoDB(从MySQL5.5.5以后,InnoDB是默认引擎),InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比Myisam的存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。

其他的引擎不支持事务等,但是存储空间占的比较小,而且操作比较快一些。

MySQL有多种存储引擎,每种存储引擎有各自的优缺点,可以择优选择使用:MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、FEDERATED、ARCHIVE、CSV、BLACKHOLE。

虽然MySQL里的存储引擎不只是MyISAM与InnoDB这两个,但常用的就是两个。

  • InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
  • MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
  • InnoDB支持外键,MyISAM不支持
  • 从MySQL5.5.5以后,InnoDB是默认引擎
  • InnoDB不支持FULLTEXT类型的索引
  • InnoDB中不保存表的行数,如select count(*) from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时MyISAM也需要扫描整个表
  • 对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引
  • 清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表
  • InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like ‘%lee%’

Go语言——struct类型作为函数参数

Posted on 2018-05-12

本文先给出Go语言中struct类型作为函数参数传入的例子,然后给出总结


首先看下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func changeValue(person Person){
person.name = "zhoujiahong"
person.age = 22
}

func changePoint(person *Person){
person.name = "zhoujiahong"
person.age = 22
}
func main() {
person := Person{
name:"origin",
}
changeValue(person)
fmt.Printf("Person:\n person.name: %s---person.age: %d\n", person.name, person.age)
changePoint(&person)
fmt.Printf("*Person:\n person.name: %s---person.age: %d\n", person.name, person.age)
}

//output:
//Person:
// person.name: origin---person.age: 0
//*Person:
// person.name: zhoujiahong---person.age: 22

总结

  • struct类型的传递是按值传递的,与数组[5]byte等的传递相似,也可以理解为和C++一样
  • struct类型传递与切片不同,切片事实上是一个指针(栈)指向一块数组(堆)这样的数据结构,所以无论如何都是传入指针或者是指针(栈)的指针(指向栈)
  • 可以理解为: 切片类似于Java(切片还多了个特色,通过传入切片可以修改指针(栈)本身,而Java是做不到的), 而数组类似于C++

注意:struct类型与切片传值方式不同而与数组相同

1…111213…20
Joe Zhou

Joe Zhou

世界上只有一种真正的英雄主义,那就是在认清生活真相之后依然热爱生活。 ——罗曼·罗兰

195 posts
38 tags
GitHub E-Mail
© 2024 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4