概述
ARTS 是耗子叔发起的编程挑战:
每周完成一个ARTS: 每周至少做一个 leetcode 的算法题、阅读并点评至少一篇英文技术文章、学习至少一个技术技巧、分享一篇有观点和思考的技术文章。(也就是 Algorithm、Review、Tip、Share 简称ARTS)
Algorithm
说白了就是一道暴搜,直接DFS搜到底就可以。因为看范围也不大,最大递归栈肯定不会超过1e6。
但是细节比较多。
个人采取了比较简单的方式,就是先处理模式串\(pp\),并新开一个标记数组\(f\),如果这个字符是可以任意个数的话就标记为1。
就比如输入的模式串为".*a*a"的话,那么处理完就是\(f:[1, 1, 0]\)和\(pp:['.', 'a', 'a']\)
那么接下来就是DFS的处理了。
最简单的情况就是模式串\(pp\)这一位不是任意的,并且模式串\(pp\)跟匹配串\(s\)这一位相同或者是'.'。
接下来就考虑如果是任意的该怎么办,其实就是从0开始到上限枚举就行了。
其实我们也很容易想出,匹配成功的情况,也就是模式串的当前匹配位跟匹配串的当前匹配位都到了最后,也就是大于等于len。要是一个大于等于而另外一个没有大于等于,那就是false了。
但是还有一个corner case,也就是因为*代表的是任意个,也就是0个也是可以的,所以当匹配串超出的时候,还需要特判模式串后面是不是都是*。
具体看代码:
func Match(s string, i int, p []byte, j int, f []int) bool {
if j >= len(p) && i >= len(s) { // 都超出肯定匹配成功
return true
}
if j >= len(p) {
return false
}
if i >= len(s) {
for k := j; k < len(p); k++ { //特判后面是不是都是*
if f[k] == 0 {
return false
}
}
return true
}
if f[j] == 1 {
if Match(s, i, p, j+1, f) { //枚举匹配为0的情况
return true
}
for k := i; k < len(s); k = k + 1 { //枚举匹配为1, 2, 3...的情况
if p[j] == s[k] || p[j] == '.' {
if Match(s, k+1, p, j+1, f) {
return true
}
} else {
return false
}
}
}
if p[j] == '.' || p[j] == s[i] {
return Match(s, i+1, p, j+1, f)
}
return false
}
func isMatch(s string, p string) bool {
var (
f = make([]int, 0, len(p))
pp = make([]byte, 0, len(p))
)
for j := 0; j < len(p); j++ { //预处理模式串
pp = append(pp, p[j])
if j+1 < len(p) && p[j+1] == '*' {
f = append(f, 1)
j++
} else {
f = append(f, 0)
}
}
//fmt.Println(f)
//fmt.Println(string(pp))
return Match(s, 0, pp, 0, f)
}
Tips
如何通过.bat一键修改与还原DNS地址?
最近要压测线上服务器。但是每次访问线上的Grafana的时候我都要手动修改DNS,之后修改完了就不能访问外网了。每次要找资料又要设置回去,在经历了一个星期的摧残之后,我总算下定决心学习用批处理去修改DNS地址。
(3条消息) 用bat修改IP、网关、DNS_weixin_34212762的博客-CSDN博客
一开始是根据这篇博客找到了算是可行的批处理脚本,之后就开始学习批处理的语句。
https://www.yiibai.com/batch_script/
之后把一开始的那个脚本的语句弄弄懂就开始自己写了。
一开始遇到了几个批处理语句相关的问题:
- @是什么作用
- > nul是什么意思
有兴趣可以自己找一下...
弄清楚了之后就开始手动写了,观察之前的脚本发现他是通过netsh这个工具去修改的,于是就去网上学习,怎么用netsh去修改DNS。
https://blog.csdn.net/u012206617/article/details/127186324
值得一提的是这个教程是有点错误的,这个参数设置了会报错
写完之后跑发现居然失败了,通过echo打印出执行语句,发现居然是因为中文乱码了(本地连接的名字叫做“以太网”)!?
最后发现是vscode的锅,bat运行的编码模式是ANSI,但是vscode貌似不支持。于是就用记事本来重新写了一遍,并保存为ANSI格式,最后成功运行。
优化:
由于涉及到底层的修改,所以一般跑.bat都需要管理员权限,每次都要右键管理员模式运行舒适麻烦。有没有让.bat自动获取管理员权限的语句呢?跟sudo差不多的
https://www.zhihu.com/question/34541107
%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c ""%~s0"" ::","","runas",1)(window.close)&&exit
学会了吗?赶紧去试试吧!
Review
在看Go的技术文章的时候,偶然间翻到了这位佬的博客https://colobu.com/
再看这篇文章的时候,看到了两个名词不是很理解:per-loop
,per-iteration
于是就去谷歌上搜...
就又来到了一个佬的博客里:https://nullprogram.com/blog/2014/06/06/
(我发现歪果仁都很喜欢js跟java)
佬主要是因为JS将per-loop
改成了per-iteration
所以谈了谈这两个模式。
稍微用我自己的话翻译一下这篇文章:
这篇文章主要是讲ECMA-262 6th中对loop的一些改动。也由此引申出了一个许多编程语言的自古以来的老问题。也就是我们最开始讲的per-loop
的问题。
就拿C语言举例:
在K&R C中,
布莱恩·柯林汉(Brian Kernighan) 和 丹尼斯·里奇(Dennis Ritchie) 出版了一本书,名叫《The C Programming Language》,人们称这个版本的 C语言为K&R C
一般的for loop包含以下四个部分,执行顺序按照:
for (INITIALIZATION; CONDITION; ITERATION) {
BODY;
}
- 执行 INITIALIZATION
- 执行 CONDITION,为假(false)则退出
- 执行BODY
- 执行ITERATION
- 回到2
C89
在C89中,一般是这么写for-loop的:
int count = 10;
/* ... */
int i;
for (i = 0; i < count; i++) {
double foo;
/* ... */
}
也就是变量i
需要先定义在loop之外。
C99
在C99中,变成了可以这样写:
int count = 10;
/* ... */
for (int i = 0; i < count; i++) {
double foo;
/* ... */
}
那我们考虑一下变量i
所属的作用域是在哪个范围?
一般我们学校所学就告诉我们,i
所在范围就是在这个for的范围内。
但是实际上for里面还有两层:
也就是变量i
是处于Loop Scope里,也就是说其实还是在foo
的外层。
所以对于foo变量来讲,C99的for-loop实现跟C89的实现其实是一样的。
JavaScript中的let
在js中,没有块作用域的概念,只有函数作用域。在最新的6th edition中,引入了let语句。通过let声明的变量将拥有块作用域。
let count = 10;
// ...
for(let i = 0; i < count; i++) {
let foo;
// ...
}
console.log(foo); // error
console.log(i); // error
闭包陷阱(The Closure Trap)
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。用人话来讲,是一个捕获外部变量,并返回这个外部变量相关的函数(一般)。
上述说的per-loop
的情况,在一般的场景里是不会导致出现错误的。但是一旦与闭包相结合之后,就会有问题。因为一般闭包设计都是,当执行到这一个函数的时候才会调用这个变量。
以下是一个Python的例子:
closures = []
for i in xrange(2):
closures.append(lambda: i)
closures[0]() # => 1
closures[1]() # => 1
Perl例子:
my @closures;
for (my $i = 0; $i < 2; $i++) {
push(@closures, sub { return $i; });
}
$closures[0](); # => 2
$closures[1](); # => 2
Ruby也是类似:
closures = []
for i in (0..1)
closures << lambda { i }
end
closures[0].call # => 1
closures[1].call # => 1
当然,解决这些问题的方法也很简单,只需要在iteration scope
中建立局部变量就行。
就像在perl里:
my @closures;
for (my $i = 0; $i < 2; $i++) {
my $value = $i;
push(@closures, sub { return $value; });
}
$closures[0](); # => 0
$closures[1](); # => 1
JavaScript的前后对比
let closures = [];
for (let i = 0; i < 2; i++) {
closures.push(function() { return i; });
}
/* Before the change: */
closures[0](); // => 2
closures[1](); // => 2
/* After the change: */
closures[0](); // => 0
closures[1](); // => 1
也就是说在新版的js中,for-loop的作用域划分会变成这样:
将会更好的服务闭包与for - loop的结合。
总结
作者仍不确定这样的改动是否合适。但是这样的fix确实是更符合程序员的逻辑。然而这个问题的修复,可能会导致之前已经记住这个机制,并且写代码就是为了捕获不变的loop scope变量的话,就会出现问题。也就是说,程序的原本可以通过自己加局部变量变成捕获局部,不加变成捕获外部。现在的话就只能统一捕获局部了。
其他的语言例如C#与Perl也有这样的问题,但是他们的解决方式不是改变for-loop而是推出了一个新的loop叫做for-each的方式来达到同样效果。
作者认为 Python 和 Ruby 的 for ... in 表单应该表现得像这样 foreach。这些可能在早期设计错误,但此时无法更改它们的语义。因为 JavaScript 的 var 从一开始就设计不正确,所以 let 提供了修复 var 的机会。
Share
阿里技术专家详解DDD系列 第四讲 - 领域层设计规范 - 知乎 (zhihu.com)主要是从群友这篇文章里看到了OOP实现,之后又想到了我现在使用的GO感觉并没有很OOP于是就去搜,golang 是面向对象的语言吗? - 知乎 (zhihu.com)。得到了回答,Go可以是也可以不是,更为通用。
之后回答里面提到了两篇教学文章:
Structs Instead of Classes - Object Oriented Programming in Golang (golangbot.com)
Methods, Interfaces and Embedded Types in Go (ardanlabs.com)
也是让人受益匪浅。
标签:...,ARTS,return,len,let,closures,loop From: https://www.cnblogs.com/Vikyanite/p/16892127.html