从0发现Lisp

一起来画画

假如说我们有一些画画的能力。这里使用JS来举个例子

drawPoint({x: 0, y: 1}, 'yellow')
drawLine({x: 0, y: 0}, {x: 1, y: 1}, 'blue')
drawCircle(point, radius, 'red')
rotate(shape, 90)
...

我们可以画一个点,画一条线,可以画圆,可以将图形旋转90度。

新的挑战

一般而言我们的这些能力我们都希望可以复用起来,不能开发一次就丢了,那就太浪费开发小哥哥的头发了。我们希望支持配置化,这意味着我们希望读取一些配置然后把不同的参数给不同的函数去执行,并得到一个结果。我们有个文件输入流,我们假定他叫stream

 stream.on('data',data =>{
   //TODO
 })

Eval

选择JS来举例子是因为JS可以使用eval,如果我们想偷懒的话可以直接使用eval来满足我们的需求。

 stream.on('data',data =>{
   eval(data)
 })

我们只要在配置里面写上"drawLine({x: 0, y: 0}, {x: 1, y: 1}, 'red')" 就可以画一条线了。 但是。。。只要稍微有点经验的程序员就会建议你不要这么做,因为你不知道data里面具体是什么,这么做就相当于让你的系统在配置面前裸奔,天晓得他会调用什么方法,会发生什么。

看来我们不能用eval了,但是我们还是需要解决我们的问题,我们想让用户可以自定义到底要怎么画画(毕竟要赋能业务嘛 🐶)

简单的想法

我们可以用JSON来做一个简单的配置,配置好要执行的函数,这个函数对应的参数是什么就可以了呀。假定我们设计好我们的配置格式如下:

{
  instructions: [
    { functionName: "drawLine", args: [{ x: 0, y: 0 }, { x: 1, y: 1 }, "blue"] },
  ];
}

我们拿到配置之后要做的就是将其翻译成对drawLine({x: 0, y: 0}, {x: 1, y: 1},"blue") 的调用就可以了。 感觉还是挺简单的。

 stream.on('data',instruction =>{
   const fns = {
    drawLine: drawLine,
    ...
  };
  data.instructions.forEach((ins) => fns[ins.functionName](...ins.args));
 })

easy!!

稍微简化一下

因为每条指令的开始都是函数名,然后都是参数,所以我们其实并不需要指出哪个是函数名,哪个是参数,我们可以将我们的配置简化如下:

{
  instructions: [["drawLine", { x: 0, y: 0 }, { x: 1, y: 1 }, "blue"]],
}

我们将我们的配置从一个json对象变成了一个数组。instructions 是一个包含指令数组的数组,一条指令的第一个对象一定是函数名,剩下的是函数的参数。修改一下我们的代码:

 stream.on('data',instruction =>{
   const fns = {
    drawLine: drawLine,
    ...
  };
  data.instructions.forEach(([fName, ...args]) => fns[fName](...args));
 })

好了,我们的drawLine 又可以欢快地跑起来了。

🤖仿生人会梦见电子羊么?

梦和现实的区别

突然意识到梦境和现实的区别是什么。现实里面所有的事情都有无限的细节可以深入,但是梦里面的事物大多是已有现实的碎片。很多的细节没法深究下去。比如你买了一双鞋子,现实里面只要你想就可以了解到这双鞋子的颜色尺码,品牌,甚至连鞋子盒子上的包装纸的纹理和保修卡上的文字你都可以知道,但是梦境里的很多都是无法深入下去的。

所以梦境和现实对大脑来说输入的精度,或者说输入的信号的信息密度是不一样的,一个是4K高清的,另一个仿佛是480p。

为什么梦没有无线的细节呢,因为梦中事物是我们保存在大脑里的记忆碎片。人不会对现实存下那么清晰的素材。

为什么要做梦呢?

类比神经网络训练的模式,将已有的素材拆分拼接,图像旋转,拉伸等方式,我们可以在有限的训练样本中获得更多的训练资料。做梦也是一样的道理,我们通过做梦把现实的碎片拼接在一起,让我们的大脑去模拟和经历一遍。这样相当于大脑经历了更多事情,得到了更多的输入。所以在漫长的进化中可能不是所有生物都做梦,但是做梦这个机制可以让人类学习得更好。

有没有其他动物会做梦呢?我们怎么知道另一种动物是不是在做梦呢?

科学家普遍相信,许多动物在睡眠时也会做梦,类似于人类。然而,要确切地知道其他动物是否在做梦是比较困难的,因为我们无法直接询问它们。目前,科学家主要通过观察动物在睡眠中的脑电波活动、眼动活动以及其他行为表现来推测它们是否在做梦。但要得出准确的结论仍需要更多的研究和科学证据。

有个经典的科幻小说问题叫做仿生人会梦见电子羊么?

why not?

仿生人🤖当然可以梦见电子羊,这也是他自我学习的过程。

Outline wiki 同步

说明

我搭建了一个outline的wiki来做自己的文档管理,然后outline wiki支持web hook 所以我写了个简单的golang脚本用来同步wiki和博客的内容,这样hugo就相当于拥有了一个可视化编辑的编辑器,也不用使用git来管理博客的更新了,这些内容都可以自动化了。下面是博客内容的一些测试。

Title

  • A
  • b

subtitle

3级标题

4级标题

##### outline wiki 不支持5级标题

c语言的代码例子

#include<iostream>
using namespace std;
int main(){

  cout<<"Hello World!!"<<endl;


  // 这是一条注释
  return 0;

}

图片的链接还需要修复,从表现上看图片在outline中是通过api接口从缓存中获取的,现在还无法直接拿到对应的图片

从pic图床拿到的图片,目前可以先试用图床。

https://pic.xn--oxap.xyz/images/2024/02/19/image.png

表格测试 4
12
2

$$ \lim_{x\rightarrow0} \frac{\sin(x)}{x} = 1 $$

分割线


2B or not 2B

遇到的问题

  1. outline中会自动添加换行用来在outline中展示换行,转换到hugo的markdown时需要处理
  2. 数学公式在outline中默认是3个$来表示,需要替换为常见的2$

需要在theme中开启下公式支持

# KaTeX mathematical formulas config (KaTeX https://katex.org/)
# KaTeX 数学公式配置 (KaTeX https://katex.org/)
[params.page.math]
  enable = true

试试看公式支持 $ \sin(x) $

房价是不是太高了

我的简单模型

简单来说可以看看如果没有这套房子你需要付出多少的成本

假如一套房子的产权是70年,总价是X。

如果全部走租房,月租是M 70 x 12 x M

一般来说房子还会带来一些其他政策上的便利,

  • 学区
  • 户口
  • 医疗

等等条件。这些价值比较难以直接得衡量。

所以只要 X < 70 x 12 x M

那么其实不算过高的价格

这可能是一个比较粗浅的模型:

还要算上对应的通货膨胀。

如果一个人不打算在一个城市长期发展,也没有人会租房70年吧,早就回家了。

专业复杂些的方式

下面是GPT给出的一个评估模型,应该是一些机构的操作评估方式。


设计一个估计房子价值的模型涉及到几个关键步骤,包括数据收集、特征选择、模型选择、训练以及评估。以下是一个基本的指南:

1. 数据收集

首先,你需要收集房地产市场的数据。这些数据通常包括房子的各种特征,例如面积、位置、建造年份、房间数量、卫生间数量、近期翻新情况等,以及房子的成交价。数据可以从公共房地产记录、在线房地产网站或通过合作伙伴获得。

2. 特征选择

从收集到的数据中选择对房价预测最有影响的特征。一些常见的特征包括:

  • 位置:通常是影响房价最重要的因素。
  • 面积:房屋和土地的大小。
  • 房间数:卧室和浴室的数量。
  • 建造年份翻新情况:新房通常比旧房贵。
  • 设施:如车库、花园、阳台等。
  • 邻近设施:如学校、公园、购物中心等。

使用相关性分析和特征重要性评估方法,如皮尔逊相关系数、斯皮尔曼等级相关、随机森林特征重要性等,来辅助特征选择过程。

3. 模型选择

选择一个或多个机器学习模型来训练数据。常用的模型包括:

  • 线性回归:适用于特征和目标之间存在线性关系的情况。
  • 决策树随机森林:可以处理非线性关系,且对特征之间的交互作用表现良好。
  • 梯度提升树(如XGBoost、LightGBM):提供高精度的预测,但需要仔细调参。
  • 神经网络:适用于大规模数据集,可以捕捉复杂的非线性关系和特征交互。

4. 训练模型

使用选择的模型和数据集来训练模型。这个过程通常包括将数据分为训练集和测试集,使用训练集来训练模型,并用测试集来评估模型的性能。

5. 模型评估和调优

使用适当的评估指标,如均方误差(MSE)、均方根误差(RMSE)或平均绝对误差(MAE),来评估模型的预测性能。根据评估结果调整模型参数或选择不同的模型进行尝试。

6. 部署和应用

一旦模型经过充分训练并验证其预测性能满足要求,就可以将其部署到生产环境中,用于实时估计房屋价值或作为房地产决策支持工具。

这个流程仅仅是一个起点,实际过程中可能需要进行多次迭代和调整。关键是持续评估模型性能并根据实际需求调整方向。


还是觉得我的估计模式简单又高效!😊

Golang的罪与罚

Go语言特点

Golang(Go)是一门出色的编程语言,以其简洁、高效和易用而受到广泛欢迎。在设计Golang时,语言的创建者们深思熟虑地融入了许多独特的设计思路和哲学,使其在各种场景下呈现出强大的功能和优势。本文将探讨Golang的语言设计以及背后的思考和哲学,并与其他语言进行横向对比。

简洁和高效的设计

Golang的语言设计注重简洁和高效。它丢弃了许多其他语言中繁琐和冗长的语法和特性,将代码精简到了最简形式。这使得Golang代码易于阅读、编写和维护,并且在运行时具有卓越的性能。与其他语言相比,Golang的编译速度非常快,这意味着开发者可以更快地构建和测试他们的应用程序。

并发编程的支持

Golang在设计中专注于支持并发编程,使得编写高效的并发代码变得更加容易。它提供了轻量级的Go协程(goroutine)和通道(channel)机制,可以用于在程序中实现并发和消息传递。通过这种方式,开发者可以更好地利用多核处理器,提高程序的性能。

内存管理

Golang在内存管理方面有独特的设计。它使用了一种称为垃圾回收(garbage collection)的机制,自动管理动态分配的内存。这减轻了开发者的负担,不再需要手动分配和释放内存。垃圾回收器可以自动识别和回收不再使用的内存,从而避免内存泄漏和许多其他内存相关的错误。

开放的标准库

Golang提供了一个丰富的标准库,其中包含许多常用的功能和工具,可以帮助开发者更轻松地构建应用程序。这些包括文件操作、网络编程、加密解密、并发编程等。通过使用这些标准库,开发者可以节省大量的时间和精力,而不必从头编写所有的功能。

goroutine

golang中最重要的特色就是goroutine。从函数式角度出发这就是用户维度的continuation,golang并不会让代码的性能变得更高,Java存在Thread和纤程之后理论上并发的处理能力并不一定比golang差。erlang也很早就能支持这种用户维度的线程。golang做得比较好的一点是做了非常方便的封装


go func(){
}

就可以启动一个新的协程

二进制编译

所有golang的代码都是可以静态编译的,启动的时候也不存在一个JVM的虚拟机需要先启动,所以相对而言对于小工程来说golang能得到一个非常小的可执行文件,内存占用也相对较小。但是这一点对于一些企业级的应用上来说还真的不一定,这时候最终打包出来的镜像和应用占用的内存消耗并不一定比java少多少,大概率还是在一个量级的。

工具链

Golang 官方提供了一系列开发工具链,这使得在vim或者emacs这样的编辑器中也能获得不错的开发体验。

学习编程语言

语言都是用来交流,描述事物的。编程语言也是一种语言,和自然语言不同的是:编程语言是准确的(即使LLM成熟了可能也很难解决自然语言二义性的问题,这是自然语言定义的本质上带来的问题)、运行在计算机(人脑也可以解析)上的一门语言,用来和计算机交流的一门语言。

既然是一门语言,和学习自然语言类似,我们在学习的时候都会经历一些相同的步骤。学习语言的基本组成(词语、语法等等);学习怎么使用这些基础组件 组合出丰富的表达能力(怎么遣词造句、组成段落);学习怎么凝练地描述一个概念。翻译上有所谓的信雅达的概念,在使用自然语言和编程语言的时候其实也存在这个问题——如何将一个事物描述得凝练准确。

提供了哪些基础能力和类型?

Golang 提供了基础类型包括:

  • string
  • int (int32 int64)
  • bool

基本的数据结构

  • array 数组,几乎所有的编程语言都提供了这种基础的数据类型,因为这就是计算机内存中一片连续的内存空间,数据的长度是固定的。
  • slice 切片,有点类似c++中的vector,是可以动态增长的数据,程序员不需要关心需要开辟多大的内存,这个数据结构可以自动扩容、缩容。
  • map

数据操作

  • make 用来新建数组或者map,还有chan

如何构建复杂的模型?

golang 提供了struct 来构建复杂的数据结构

// 直角坐标点
type Point struct{
    x float64,
    y float64
}

// 极坐标点
type RPoint struct{
    // 半径
    r float64,
    // 角度
    d floadt64
}

// 线段,两点确定一个直线
type Line struct{
    a Point,
    b Point,
}

通过struct可以构造出复杂的数据结构来给各种业务进行建模,对比java的方式是通过class来定义数据结构。class可以认为是struct的扩展,但是java本身的设计限制了像c++那样的多重继承,而且java的设计模式中也不推荐过于依赖继承的方式来扩展数据模型,多重继承也是被限制的。(组合优于扩展) 那么要怎么扩展和一个已有的结构呢?

My Will (遗嘱)

没错,这是一篇关于遗嘱的博客。我没有遇到什么问题,恰恰相反,我现在活得好好的并且热爱着我现在的生活。正是因为我感激着生活带给我的快乐,所以我想在这片博客中写下自己的遗嘱。你永远不知道意外和明天哪一个先到来。