#Mathematics #PL #Math #learn
+ 数学上的分段函数
还是看 PDF 吧。TeX 命令也可以贴不过我懒得贴了(因为我想找在线的即时预览编辑器,没找到能用的 hhhh
https://cn.overleaf.com/ 慢死了
116 面 e6. Haskell. 等价,虽然我不是特别熟悉 Haskell 所以有余赘
或者我们可以更 less-boilerplate 一些,利用 Haskell 的 range
好吧,这个不会 terminate,因为我用的是 -Infinity... 而 Haskell 不知道我到底想说啥... 因为我也不咋会 Guard pattern
让我们再看看 Haskell 里的 Guard pattern
是类似的。不过这里 Haskell 的分布函数有个 branch exhaustivness check 的问题,比如 Fib 函数
但这个 Guard pattern 版本
不 total(完全)(比如,输入 -1 就没有值),不 check。(咦?怎么能过?不 sound?)(之前 GHCi 过不了就是)
那么一定要用就可以上
+ 啥是 tailrec #Haskell #functional
首先啥是递归
Y = λf. (λx. f (x x)) (λx. f (x x))
(Y Y)
我们先来进行 β - 归约... 算了看过程
首先我们 f (x x) [ f := Y, x := (λx. f (x x)) ] 看不懂算了,因为数学的东西就是为了形式化,不易读的... 所以不数学算了(
(λx. f (x x)) (λx. f (x x)) where f = y 被替换为 (λx. Y (x x)) (λx. Y (x x))
然后接着展开... 唉头大,总之,类似这样就可以了,学个一半然后自己再学也没什么
首先 apply
... 到
apply... (self self) where self = (λself. (self self))
((λself. (self self))
(λself. (self self)))
又是它。无限循环。infinity recursion。结束分析....
然后看看这个 ES6 #JavaScript 的 tco 优化器,汲取灵感 <del>所以 ES6 还是很 functional 的</del>
然后我们看看这个伪代码例子
tco 就是尾调用优化,如果一个函数的最后一步是调用自身或者兄弟函数(这个 case 目前请无视)就可以进行尾调用优化
这样调用栈帧就可以不保存,就不会 ~~上~~ 栈爆 ~~Stack Overflow~~ 了
其实我们发现这种递归可以优化为循环的(试试用循环 + 变量解决它),所以 tco 允许我们用递归的方式写循环
+ 过程和结果。副作用和返回值
有的时候我们更注重过程而不是结果,比如... 比如... (突然害羞)(迫真)生活中例子很多
比如....
nil
import System.Directory
import System.IO
lsdir :: FilePath -> IO [FilePath]
lsdir = listDirectory
fexists :: FilePath -> IO Bool
fexists = doesFileExist
data FTree = FTree [FTree] | Atom FilePath deriving (Eq, Read)
ftree :: FilePath -> FTree
instance Show FTree where
show FTree fs = show fs
show Atom fp = show fp
ftree = undefined
listDirectory "." >>= print
doesFileExist "/proc/cmdline" >>= print
+ 数学上的分段函数
还是看 PDF 吧。TeX 命令也可以贴不过我懒得贴了(因为我想找在线的即时预览编辑器,没找到能用的 hhhh
https://cn.overleaf.com/ 慢死了
116 面 e6. Haskell. 等价,虽然我不是特别熟悉 Haskell 所以有余赘
p(h) | (h >= 0) = 7 * h
| (h <= 40) = 7 * h
| h > 40 = 10.5 * h - 140
或者我们可以更 less-boilerplate 一些,利用 Haskell 的 range
p h
| elem h [-1/0..40] = 7 * h
| h > 40 = 10.5 * h - 140
好吧,这个不会 terminate,因为我用的是 -Infinity... 而 Haskell 不知道我到底想说啥... 因为我也不咋会 Guard pattern
main = print $ p <$> [0..300]
然后至于数据画函数图像嘛可以随便找个软件比如 LibreOffice 什么的,KDE 也有转们的软件,要不然 Kotlin JavaAWT JPanel
也可以。让我们再看看 Haskell 里的 Guard pattern
是类似的。不过这里 Haskell 的分布函数有个 branch exhaustivness check 的问题,比如 Fib 函数
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
main = print $ fib 1
但这个 Guard pattern 版本
fib :: Int -> Int
fib n | n == 1 = 1
| n == 2 = 2
| otherwise = (fib n - 1) + (fib n - 2)
main = print $ fib 0
不 total(完全)(比如,输入 -1 就没有值),不 check。(咦?怎么能过?不 sound?)(之前 GHCi 过不了就是)
那么一定要用就可以上
undefined
(+ 啥是 tailrec #Haskell #functional
首先啥是递归
Y = λf. (λx. f (x x)) (λx. f (x x))
(Y Y)
我们先来进行 β - 归约... 算了看过程
首先我们 f (x x) [ f := Y, x := (λx. f (x x)) ] 看不懂算了,因为数学的东西就是为了形式化,不易读的... 所以不数学算了(
(λx. f (x x)) (λx. f (x x)) where f = y 被替换为 (λx. Y (x x)) (λx. Y (x x))
然后接着展开... 唉头大,总之,类似这样就可以了,学个一半然后自己再学也没什么
(λf. (f f))
(λself. (self self))
首先 apply
(λself.
... 到
(lambda f.
(λself. (self self))
(λself. (self self))
apply... (self self) where self = (λself. (self self))
((λself. (self self))
(λself. (self self)))
又是它。无限循环。infinity recursion。结束分析....
然后看看这个 ES6 #JavaScript 的 tco 优化器,汲取灵感 <del>所以 ES6 还是很 functional 的</del>
function tco(fun) {
var value = null, active = false, accumlated = [];
return function accumlator() {
accumlated.push(arguments);
if (!active && (active = true)) { // 是的,我利用 && 非短路语义压行了
while (accumlated.length != 0) value = fun.apply(this, accumlated.shift());
active = false; return value;
}}} // 是的,Sexp 系写多了的结果...
sum = tco((x, y) => (y > 0) ? sum(x + 1, y - 1) : x);
console.log(sum(1, 100)) // 101
然后我们看看这个伪代码例子
type <A : Comparable<in A>> binarySearch : |A[]| |A| |Range| Int
binarySearch xs o <srch = xs.indexRange> = |>
let index = srch.mid, guess = xs[index] in when
| srch.len zero -> nil
| guess is o -> index
| guess > o -> tailrec(srch.a..srch.mid.sub1)
| guess < o -> tailrec(srch.mid.add1..srch.b)
tco 就是尾调用优化,如果一个函数的最后一步是调用自身或者兄弟函数(这个 case 目前请无视)就可以进行尾调用优化
这样调用栈帧就可以不保存,就不会 ~~上~~ 栈爆 ~~Stack Overflow~~ 了
其实我们发现这种递归可以优化为循环的(试试用循环 + 变量解决它),所以 tco 允许我们用递归的方式写循环
+ 过程和结果。副作用和返回值
有的时候我们更注重过程而不是结果,比如... 比如... (突然害羞)(迫真)生活中例子很多
比如....
puts "Hello, world!" #=> nil
class Foo; end
macro_rules! foo {
() => { "foo" };
};
`
nil
我们要你何用?
() 我们要你何用?
我们要的是
puts 调用产生的副作用,向
STDOUT 写出一行你好世界
我们要的是
class Foo 的副作用,定义一个
Foo 类赋值到
Foo 常量上
我们要的是
macro_rules! 宏的副作用,它把一个用户定义的宏注册到系统里面去以便使用
副作用在很多语言里都是 invisible 的,只有返回值和异常什么的可以在抽象函数调用之间传递
这也一般无可厚非,因为很多时候我们不要返回值只需要副作用而已,他们两个还是很不一样的
不过 Haskell 这完全 pure 的语言里面有 IO 类型,不过 @ice1000 都说他啥时候还觉得 Haskell 的 IO 不好理解我也不敢随便说话了
纯函数就是没有副作用的函数,一般数学函数都属于此类,纯函数不是什么不用
printf 也不是什么不用局部变量,它是说
+ 函数本身运行稳定可预期
+ 函数的运行不对外部环境产生影响
+ 函数的数据流全是显示的,就是说函数与外界环境交换信息只能使用它的参数(包括
this)和它的返回值
纯函数的好处是
+ 无状态,无需考虑线程安全
+ 纯函数的组合还是纯函数
+ 相同参数永远相同结果,可以缓存计算结果、延迟求值、改变求值顺序、改变求值环境、多次求值也可以随意
当然别处还有更严谨的定义,不过我不纠结这些就是了...
+ 扫描到树状图以及输出(挂起中,因为... 还有事...)
... 因为 UNIX 的 fs 文件系统都是比较方便的吧... 我们拿这个做例子快点讲了好办事
Haskell. 我们先来看看我们的需要的 API 抽象
``
import System.Environment import System.Directory
import System.IO
lsdir :: FilePath -> IO [FilePath]
lsdir = listDirectory
fexists :: FilePath -> IO Bool
fexists = doesFileExist
data FTree = FTree [FTree] | Atom FilePath deriving (Eq, Read)
ftree :: FilePath -> FTree
instance Show FTree where
show FTree fs = show fs
show Atom fp = show fp
ftree = undefined
首先我们需要一个 API 来拿到文件夹子树
listDirectory "." >>= print
然后我们需要一个 API 判断『对象(路径表示的)』是文件还是目录(里面还有文件)
doesFileExist "/proc/cmdline" >>= print
`
OK. 那么我们来实现 `ftree`Overleaf
Overleaf, 在线LaTeX编辑器
一个简洁的在线 LaTeX 编辑器。无需安装,实时共享,版本控制,数百免费模板……