【Scheme】Scheme 编程学习 (七) —— Macros 宏


原视频地址: https://www.bilibili.com/video/BV1Kt411R7Wf?p=7

Macros in scheme
Scheme 语言中的宏,一般我们会在各种编程语言中见到宏,在 Scheme 中也是同样的 idea

I - 问题 (The problem)

(define num 3)
; 定义符号 num 为 3
(cond
	((zero? num) (display "Z"))
	((positive? num) (display "P"))
	(else (display "N")))

设置一个条件判断,如果 num 为 0 ,则打印 Z (z 代表 zero 零),若为正数则打印 P (p 代表 positive 正数) , 其他则打印 N (n 代表 negative 负数)

II - 隐藏复杂度 (Hiding complexity)

  • How can we avoid repeating ourselves?
    如何避免重复,编程通用的方式是 DRY (don’t repeat yourself 不要重复你自己)
  • How can we speak at the right level?
    如何在一个高的层面 (in a high level) 使用 Scheme

避免重复的原因是,我们一次表达出了一个概念 (express a concept),我们不需要在每次使用的时候都重新表达这个概念 (without re-express it every time),另一种原因是,我们不必要去思考底层实现 (implementation),转而去思考更高层面的问题。

仅用代码实现一次 (code your concept once),其他时候只需要引用 (refer to) 它,这里我们希望做的事是避免 (avoid) 重复代码中的条件表达式 (cond expression),do once and leave it alone.

在编程语言中 (In program languages), 有多种方式 (several way) 来实现

  • Functions 编写函数
  • Classes 使用类
  • Extending the language 扩展语言
    • Code generation 程序生成代码
      (比如 Qt 扩展了 C++,并在编译时使用元对象系统生成 moc 相关的C++代码)
    • Macros 宏

III - 初次尝试 (First attempt)

(define (3-state
		value
		positive-body
		zero-body
		negative-body)
	(cond
		((zero? value)		zero-body)
		((positive? value)	positive-body)
		(else				negative-body)))

我们定义一个函数 3-state 需要四个入参 value, positive-body, zero-body, negative-body
这个函数体是一个条件表达式 (cond expression)

尝试调用

(3-state
	100 (display "P") (display "Z") (display "N"))
; PZN

这种方式定义与调用会执行所有的 Scheme 语句,所以我们无法直接定义函数来实现。

IV - 解决方案 (A solution)

因此我们需要定义宏 (macros),定义 syntax ,

(define-syntax
	3-state
	(syntax-rules ()
		((3-state
			value positive-body zero-body negative-body)
		(cond
			((zero? value)		zero-body)
			((positive? value)	positive-body)
			(else				negative-body)))))

第二个参数为 syntax-rules ,这里是需要做的匹配和需要扩展的模板 (the matching you should do, the template you should expand), 使用模式匹配 (pattern),并使用内容替换 (substitute in code)

> (3-state
	100 (display "P") (display "Z") (display "N"))
; P

调用

> (3-state
	0 (display "P") (display "Z") (display "N"))
; Z
> (3-state
	-100 (display "P") (display "Z") (display "N"))
; N

V - 其他的实现方式 (How else could we do this?)

在其他语言中如何实现

5.1 - 宏 (Macros)

在 C 中使用 Macros 举例如下

/* MACROS */

#define THREESTATE(VALUE,PBODY,ZBODY,NBODY) \
	{\
		int v = (VALUE); \
		if (v == 0) 	{ (ZBODY); }\
		else if (v > 0) { (PBODY); }\
		else 			{ (NBODY); }\
	}

int main()
{
	THREESTATE(3, print("P\n"), print("Z\n"), print("N\n"));
	return 0;
}

macros 为 literal expression 无法调试 (debug),
如何在 scheme 中使用 substitute

5.2 - 引用 (Quoting)

这是一部分 JavaScript 代码,函数体使用字符串来定义 (body express in strings),通过 eval 函数来调用。

function threeState( value, pbody, zbody, nbody )
{
	if (value === 0) 	{ eval( zbody ); }
	else if (value > 0) { eval( pbody ); }
	else 				{ eval( nbody ); }
}

threeState(
	3, "print('P')", "print('Z')", "print('N')" );

5.3 - 函数指针 (Function pointers)

def three_state(
		value, p_body_fn, z_body_fn, n_body_fn):
	if value == 0:  z_body_fn()
	elif value > 0: p_body_fn()
	else:			n_body_fn()

def pr( x ):
	def ret():
		print x
	return ret

three_state(3, pr("P"), pr("Z"), pr("N"))

5.4 - 类 (class)

class TestJava {
	
	interface IThreeStateBodies {
		void positiveBody();
		void zeroBody();
		void negativeBody();
	}

	static void threeState(
			int value, IThreeStateBodies bodies ) {
		if ( value == 0 ) 		bodies.zeroBody();
		else if ( value > 0 )	bodies.positiveBody();
		else					bodies.negativeBody();
	}
	
	public static void main( String[] args ) {
		threeState( 3,
			new IThreeStateBodies() {
				public void positiveBody() {
					System.out.println( "P" );
				}
				public void zeroBody() {
					System.out.println( "Z" );
				}
				public void negativeBody() {
					System.out.println( "N" );
				}
			}
		);
	}

Java 通过定义 Interface

10-31 09:01