解答:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//
// https://leetcode.com/problems/wildcard-matching/
//
// m[i, j] is the result of pattern end with p[i - 1] and string end with s[j - 1]
// if p[i - 1] == s[j - 1] or p[i - 1] = '?', m[i - 1][j - 1]
// if p[i - 1] = '*', m[i - 1][j] || m[i][j - 1] || m[i - 1][j - 1]
// else false
bool isMatch(char* s, char* p) {
size_t sl = strlen(s), pl = strlen(p);
bool **m = malloc((pl + 1) * sizeof (*m));
for (size_t i = 0; i <= pl; i++) {
m[i] = malloc((sl + 1) * sizeof (**m));
}
memset(m[0], false, sl);
m[0][0] = true;
for (size_t i = 1; i <= pl; i++) {
for (size_t j = 0; j <= sl; j++) {
bool ja = j > 0;
if ((ja && p[i - 1] == s[j - 1]) || p[i - 1] == '?') {
m[i][j] = m[i - 1][j - 1];
continue;
}
if (p[i - 1] == '*') {
m[i][j] = m[i - 1][j] || (ja ? m[i][j - 1] || m[i - 1][j - 1]: false);
continue;
}
m[i][j] = false;
}
}
return m[pl][sl];
}
文章:https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1
一种在golang开发应用的包组织方式。这样设计的理由:先设计领域语言,然后分离依赖,接着引入mock分离测试,最后在main包中把所有的东西拼接在一起。
主要有4条建议:
- 根包仅仅包含领域类型以及相关的接口定义
- 程序的每个依赖对应一个子包,比如基于mongdb实现
- 共享一个mock接口的子包
- main包把相关的依赖组合在一起
例如:
app/
user.go
mongodb/
mock/
http/
cmd/
app/
main.go
appctl/
main.go
- 根包是
app
在user.go
定义User
类型以及相应的接口UserService
。 UserService
的mongodb实现,放在子包mongodb中,暴露的http接口放在http中。- 子包
mock
中包含共享的一个mock的UserService
,里面默认的都是一些空方法,被其他测试时引用。 main
这个包负责构建http实例、UseService
实例把这些合在一起构成一个app。
meld用来做git的mergetool,做三方合并,可以一个开发人员把另一个开发人员的代码误删的情况。
又名LSP(Liskov Substitutiion Principle)。基类能够被子类代替,并且保证程序行为不变。
OCP的实现需要使用抽象和多态,静态语言中继承是多态的一个重要实现方式。LSP就是解决继承带来的一些问题,比如侵入性、耦合性、缺乏灵活性。遵守LSP能够更加容易遵守OCP,因为子类可以替换基类,达到不修改原来代码,通过扩展的方式,添加逻辑。提高程序的健壮性,版本升级的兼容性。
继承中常说的IS-A,强调的是方法的行为,子类中的方法行为要和基类中的一致,而不是性质一致。这个行为需要从设计的使用者角度来判断模块。模块逻辑的一致性,说的就是这个行为需要一致。所以,IS-A语义是子类替换时,保证程序行为一致。
虽然这里LSP强调代码中的继承,其实LSP也适用于其他约定的服务、组件,这些内容修改、替换以后都不应该影响原来程序的行为。
几个比较好的实践:
- 当子类中override的方法工作比较少时,可能违反LSP。
- 采用DBC(design by contract)编程方法。约定方法的前置条件和后置条件,在LSP下,子类中的前置条件只能比基类的弱,而子类中的后置条件只能比基类的强。因为,如果子类中的前置条件强,那么替换以后原来基类的前置条件下的输入就没法满足了,同样如果子类的后置条件弱,那么方法的输出在一些情况下程序行为就会和原来的不一样。
又名DIP(Dependence Inversion Principle)。高层不依赖底层,依赖抽象,底层也只依赖抽象;抽象不依赖细节,细节依赖抽象。
反转(inversion)包含两层含义:
- 控制流和源代码依赖相反,a模块执行时会调用b模块函数,但是源代码层面来说b模块会依赖a模块。
- 接口所有者,原先a模块使用b模块定义的接口,而现在接口放在了a模块中,从而从源代码层面来说b模块依赖a模块。
为什么要依赖抽象?显然抽象比实现细节稳定。从编程语言角度上来说,接口变了实现不变,而实现变了,接口不一定变,显然接口更加稳定。因此,接口的稳定也十分重要。
DIP能够减少类、模块之间的耦合,提供系统的稳定性,提高代码的复用性、可扩展性、可读性和可维护性。它是其他OO设计技巧的基础。
建立依赖的方式:
- 构造函数传递依赖对象。
- setter方法传递对象。
- 接口声明依赖对象,接口中的方法参数、返回值中引用其他接口。
几个比较好的实践:
- 每个类尽量有接口或者抽象类。
- 变量的表面类型尽量是接口、抽象类型或者是不易变的类。
- 任何类不从易变的具体类派生。在维护代码的时候这个实践经常会被破坏。
- 尽量不要override基类的方法。
- 创建对象时考虑使用工厂模式。