`
qiezi
  • 浏览: 498602 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

lambda之路...

    博客分类:
  • D
阅读更多
DMD最近的版本号加入了闭包,感觉非常有用,虽然有些背后动作,不过我是实用派不介意这个。玩的时候忽然想到为什么没有lambda呢?AST还没影,不过可以利用D强大的模板可以使用字符串来先模拟一下。

我假想的语法是这样的:
int[] arr = [1,2,3];
int[] arr1 = arr.map(lambda!("int x -> x * x"));


上面执行的arr1结果将会是[1,4,9]。

在编写过程中发现匿名委托不能够使用模板来这样生成:
template labmda(string expr)
{
    auto lambda = mixin("(int x){return (x);}");
}

所以必须在使用的地方去mixin(如果你可以避免这个请告诉我),像这样的:

int[] arr1 = arr.map(mixin("(int x){return (x);}"));

测试很长时间没有找到突破方法去掉这个mixin,所以就在这个起点上向目标前进了。

测试过程中也发现现在的模板对于字符串参数似乎不像以前那么友好了,编译期执行函数则得到了加强,所以使用它来实现:
int indexof(string s, char ch)
{
	foreach(i, c; s)
		if(c == ch)
			return i;
	return -1;
}

int indexof(string s, string sub)
{
	for(int i=0; i<s.length-sub.length; i++)
	{
		if (s[i..i+sub.length] == sub)
			return i;
	}
	return -1;
}

string _RetType(string expr)
{
	int end = indexof(expr, '|');
	if (end == -1)
		return "";
	else
	{
		int end1 = indexof(expr, "->");
		assert(end1 > 0, "'->' not found in lambda expression: " ~ expr);

		if (end > end1) return "";

		return expr[0..end];
	}
}

string _ArgTypes(string expr)
{
	int start = indexof(expr, '|');

	int end = indexof(expr, "->");
	assert(end > 0, "'->' not found in lambda expression: " ~ expr);

	if (start > end) start = -1;

	return expr[start+1 .. end];
}

string _Body(string expr)
{
	int start = indexof(expr, "->");
	assert(start > 0 && start<expr.length-1, "'->' not found in lambda expression: " ~ expr);

	return expr[start+2 .. $];
}

string lambda(string expr)
{
	return
		"delegate " ~ _RetType(expr) ~ 
		"(" ~ _ArgTypes(expr) ~ "){return (" ~
		_Body(expr) ~ ");}";
}

它支持的lambda语法是这样的:
//参数列表 -> 表达式:
int x -> x
int x, int y -> x * y

//返回值 | 参数列表 -> 表达式:
string | int x -> toString(x)

测试:
T[] map(T,T1)(T1[] arr, T delegate(T1) dg)
{
	T[] result;
	result.length = arr.length;
	foreach(i, v; arr)
		result[i] = dg(v);
	return result;
}

void main()
{
	int[] arr = [1,2,3];
	// int[] arr1 = arr.map(int x -> x * x);
	int[] arr1 = arr.map(mixin(lambda("int x -> x * x")));
	writefln(arr1);

	string sep = ";\n";
	string[] arr2 = arr.map(mixin(lambda("string | int x -> toString(x) ~ sep")));
	writefln(arr2);
}

运行结果:
引用

[3 6 9]
[1;
2;
3;
]

语法罗嗦了一些,要是有AST宏不知道是否能简化一些。。

加一个例子:
T1 inject(T,T1)(T[] arr, T1 init, T1 delegate(T1, T) dg)
{
	T1 value = init;
	foreach(e; arr)
		value = dg(value, e);
	return value;
}

void main()
{
	int[] arr = [1,2,3];

	// int total = arr.inject(0, (int sum, int elem) -> sum + elem);
	int total = arr.inject(0, mixin(lambda("int sum, int elem -> sum + elem")));
	writefln(total);
}


运行得到的结果是6.

对应的ruby代码:
total = [1,2,3].inject(0){|sum, elem| sum + elem}


从这个例子的注释中可以看到,lambda的参数应该用括号包起来,和参数加以区别;而单参数的lambda则应该省掉括号让它更美观,我上面的解析部分还不包括括号的解析。
分享到:
评论
15 楼 qiezi 2007-11-10  
oldrev 写道
引用

这个好像是类型反推了,估计有点困难。

仔细想了一下,确实是不行,不过返回值类型确实是可以省略的。

返回值是可以省略,现在也支持,不过在D这种强类型系统里面,有时候需要明确指定返回类型来进行精确匹配,减少程序员犯错误的机会,比如:
	char c = 'a';
	auto i = c & 0xff;
	writefln(typeid(typeof(i)));

新手很容易认为i是个char类型,实际上却是个int,所以可选的返回值类型我感觉是必要的。
14 楼 oldrev 2007-11-10  
引用

这个好像是类型反推了,估计有点困难。

仔细想了一下,确实是不行,不过返回值类型确实是可以省略的。
引用

从函数里面返回delegate在D1。0上应该也是不安全的吧。

因为这个 delegate 无法触及任何作用域以外的东西,就跟递归调用一样。
13 楼 qiezi 2007-11-10  
oldrev 写道
引用

qiezi

对了,你上面这种消除mixin的手法也只有在最新的DMD上才能进行吧,这是一个闭包而不是内嵌函数或委托量。

另外改用你这个版本以后,前两个用法都能通过,第三个有点问题:
代码

   1. string sep = ";\n"; 
   2. string[] arr2 = arr.map(lambda!("string | int x -> toString(x) ~ sep")); 
   3. writefln(arr2); 


编译无法通过,因为返回的闭包被包裹了一层,上下文已经不对了。


我的办法在 D 1.0 上也是可行的(GDC 0.24测试),附带的好处是可以防止1.0 的闭包问题,当然,定义处上下文是不能处理了。

D的delegate字面值允许省略返回值类型,所以 _RetType 也可以省掉了。

从函数里面返回delegate在D1。0上应该也是不安全的吧。
12 楼 qiezi 2007-11-10  
oldrev 写道
可不可以再改进一下,允许省略参数类型和返回值类型,做到:
"x -> x*x"

这个好像是类型反推了,估计有点困难。如何从这个委托所处的上下文获得它应该成为的类型?
11 楼 oldrev 2007-11-10  
引用

qiezi

对了,你上面这种消除mixin的手法也只有在最新的DMD上才能进行吧,这是一个闭包而不是内嵌函数或委托量。

另外改用你这个版本以后,前两个用法都能通过,第三个有点问题:
代码

   1. string sep = ";\n"; 
   2. string[] arr2 = arr.map(lambda!("string | int x -> toString(x) ~ sep")); 
   3. writefln(arr2); 


编译无法通过,因为返回的闭包被包裹了一层,上下文已经不对了。


我的办法在 D 1.0 上也是可行的(GDC 0.24测试),附带的好处是可以防止1.0 的闭包问题,当然,定义处上下文是不能处理了。

D的delegate字面值允许省略返回值类型,所以 _RetType 也可以省掉了。
10 楼 oldrev 2007-11-10  
可不可以再改进一下,允许省略参数类型和返回值类型,做到:
"x -> x*x"
9 楼 qiezi 2007-11-10  
修改了一下,现在支持这种用法:
	int[] arr = [1,2,3];
	int result = arr.inject(0, mixin(lambda("int r, int c -> r | c")));
	writefln(result);


"|" 在 "->" 后面。
8 楼 oldrev 2007-11-10  
引用
# // 匹配 int | int x, int y -> x * y 
# macro(TYPE, "|", PARAM_LIST, "->", EXPR) 


这估计是不行的,语法分析阶段无法识别类型
7 楼 qiezi 2007-11-10  
对了,你上面这种消除mixin的手法也只有在最新的DMD上才能进行吧,这是一个闭包而不是内嵌函数或委托量。

另外改用你这个版本以后,前两个用法都能通过,第三个有点问题:
	string sep = ";\n";
	string[] arr2 = arr.map(lambda!("string | int x -> toString(x) ~ sep"));
	writefln(arr2);

编译无法通过,因为返回的闭包被包裹了一层,上下文已经不对了。
6 楼 qiezi 2007-11-10  
对了,这个实验的初衷是看到邮件列表中有人对比D的闭包和其它语言的闭包语法,D虽然语法也够简炼了,不过比起来还是显得罗嗦,比如:
// ruby:
arr = [1,2,3]
total = arr.inject(0){|sum, elem| sum + elem}

// D:
int[] arr = [1,2,3]
int total = arr.inject(0, (int sum, int elem){return sum + elem;});

主要是丑在大括号这部分,小括号里面包着大括号感觉很怪。
5 楼 qiezi 2007-11-10  
oldrev 写道
貌似 AST 宏也不是很难实现,我的设想是编译器遇到 AST 宏就把它记录到一张表里,并为宏里的代码生存一棵单独的 AST。当整个程序的代码都生成 AST 以后,再对代码 AST 进行模式匹配,然后把宏体内的 AST 嫁接上去就行了。

除了 The Future of D 里的演示外,应该再往前走一步,允许无名宏:
macro("for_each", "(", value, "in", container, ")", body)
{
	auto iter = container.begin();
	for(; iter != container.end(); ++iter)
	{
		auto value = iter.value();
		body; 
	}
}

int[] array = [1,2,3,4,5,6];

for_each(i in array)
	writefln(i);	


这个是很有必要,不知道Walter打算如何实现。另外对于一些常用的匹配应该提供一些内置的名称,类似下面的TYPE/PARAM_LIST/EXPR:
// 匹配 int | int x, int y -> x * y
macro(TYPE, "|", PARAM_LIST, "->", EXPR)

// 匹配 (int x, int y) -> x * y
macro("(", PARAM_LIST, ")", "->", EXPR)

// 匹配 int x -> x * x
macro(PARAM_LIST, "->", EXPR)

还有变长部分的匹配,最好与可变参数一致。
4 楼 qiezi 2007-11-10  
哈哈,不错!这么曲溜拐弯的。。往前走了一步。

在没有AST macro的情况下,估计也只能写成这样了。
3 楼 oldrev 2007-11-10  
程序里有个错误,请把第11行的 size_t 改成 int
2 楼 oldrev 2007-11-10  
当当当,经过一个半小时的努力,终于去除了 mixin!
import std.stdio;

int indexof(string s, char ch)  
{  
    foreach(int i, c; s)  
        if(c == ch)  
            return i;  
    return -1;  
}  

size_t indexof(string s, string sub)  
{  
    for(int i = 0; i < s.length - sub.length; i++)  
    {  
        if (s[i .. i + sub.length] == sub)  
            return i;  
    }  
    return -1;  
}  

template _RetType(string expr)
{
    static if (indexof(expr, '|') == -1) 
        const string _RetType =  "";  
    else
        const string _RetType = expr[0..indexof(expr, '|')];  
}

template _ArgTypes(string expr)
{
	const string _ArgTypes = expr[indexof(expr, '|') + 1 .. indexof(expr, "->")];
}

template _Body(string expr)
{
	const string _Body = expr[indexof(expr, "->") + 2 .. $];
}


template _LambdaLiteral(string expr)
{
	const string _LambdaLiteral = "delegate " ~ _RetType!(expr) ~ 
		"(" ~ _ArgTypes!(expr) ~ "){return (" ~  _Body!(expr) ~ ");}";
}

template lambda(string expr)
{
	typeof(mixin(_LambdaLiteral!(expr))) lambda()
	{
		return mixin(_LambdaLiteral!(expr));
	}
}


T1 inject(T,T1)(T[] arr, T1 init, T1 delegate(T1, T) dg)  
{  
    T1 value = init;  
    foreach(e; arr)  
        value = dg(value, e);  
    return value;  
}  

void main()  
{  
    int[] arr = [1,2,3];  
    int total = arr.inject(0, lambda!("int sum, int elem -> sum + elem"));
    writefln(total);  
}  


输出:
6


缺点是错误检测还没做,不过这是小问题。
1 楼 oldrev 2007-11-10  
貌似 AST 宏也不是很难实现,我的设想是编译器遇到 AST 宏就把它记录到一张表里,并为宏里的代码生存一棵单独的 AST。当整个程序的代码都生成 AST 以后,再对代码 AST 进行模式匹配,然后把宏体内的 AST 嫁接上去就行了。

除了 The Future of D 里的演示外,应该再往前走一步,允许无名宏:
macro("for_each", "(", value, "in", container, ")", body)
{
	auto iter = container.begin();
	for(; iter != container.end(); ++iter)
	{
		auto value = iter.value();
		body; 
	}
}

int[] array = [1,2,3,4,5,6];

for_each(i in array)
	writefln(i);	

相关推荐

    lambda-dg.pdf

    AWS Lambda 是一个无需管理服务器即可运行代码的服务。开发者可以在 AWS Lambda 上运行代码,以响应事件。AWS Lambda 能够自动扩展以应对数以千计的并发请求,而无需任何关于资源的管理操作。AWS Lambda 会根据预...

    Stream、Lambda表达式练习.doc

    Stream和Lambda表达式实践 在Java中,Stream API是Java 8中引入的一种新的数据处理方式,它可以对集合进行各种操作,如过滤、映射、聚合等。Lambda表达式是Java 8中引入的一种新的函数式编程方式,它可以将函数作为...

    C#lambda表达式入门.pptx

    通过多个示例详细演示了.Net中C#语言lambda表达式的基本用法,主要包括三个方面内容: 1.什么是lambda表达式 2.理解lambda表达式 3.使用lambda表达式

    Python库 | aws-cdk.aws-lambda-1.25.0.tar.gz

    alias = _lambda.Alias(self, "MyAlias", version=lambda_function.current_version, alias_name="prod") ``` aws-cdk.aws-lambda-1.25.0库为Python开发者提供了强大的工具,简化了AWS Lambda函数的创建和管理...

    PyPI 官网下载 | mypy-boto3-lambda-1.17.48.0.tar.gz

    from mypy_boto3_lambda.service_resource import LambdaClient lambda_client = LambdaClient() response = lambda_client.get_function(FunctionName='myFunction') ``` 在这里,`mypy_boto3_lambda`库会确保...

    Python库 | mypy-boto3-lambda-1.17.58.0.tar.gz

    标题中的"mypy-boto3-lambda-1.17.58.0.tar.gz"是一个针对Python开发的库,这个库集成了mypy、boto3和Lambda相关的功能。让我们详细了解一下这些关键组成部分。 **mypy**: mypy是Python的一个静态类型检查工具。在...

    lambda-3.0.zip

    LAMBDA算法,全称为Linear Ambiguity Resolution by Decorrelation in the Least-SquaresAmbiguity Decorrelation Adjustment,是由荷兰代尔夫特理工大学的Tijmen T. Bouma于1996年提出的一种用于全球导航卫星系统...

    Python库 | aws-cdk.aws-lambda-1.32.2.tar.gz

    Python库aws-cdk.aws-lambda-1.32.2.tar.gz是AWS Cloud Development Kit (CDK)的一个组件,专门用于构建和管理AWS Lambda函数。这个压缩包包含了版本1.32.2的库,Lambda在AWS服务生态中扮演着核心角色,它是一种无...

    PyPI 官网下载 | aws_lambda-1.0.0.tar.gz

    result = aws_lambda.process_event(event) return { 'statusCode': 200, 'body': result } ``` 在完成开发和测试后,可以使用`aws_lambda`库提供的工具或AWS SDK将Lambda函数部署到AWS。 总结来说,`aws_...

    PyPI 官网下载 | mypy-boto3-lambda-1.12.12.0.tar.gz

    《PyPI官网下载:mypy-boto3-lambda-1.12.12.0.tar.gz详析》 PyPI(Python Package Index)是Python开发者的重要资源库,它提供了大量的Python库,使得开发者能够方便地下载、安装和分享Python项目。在本篇文章中,...

    lambda表达式专题.pdf

    lambda表达式专题.pdf

    Python库 | aws-cdk.aws-lambda-1.6.1.tar.gz

    标题中的"aws-cdk.aws-lambda-1.6.1.tar.gz"是一个针对Python的库,主要用于AWS(Amazon Web Services)的CDK(Cloud Development Kit)框架,特别是处理AWS Lambda功能的部分。AWS Lambda是AWS提供的一个无服务器...

    Lambda表达式终.zip

    Lambda表达式是Java编程语言中的一个关键特性,自Java 8引入以来,它极大地简化了处理函数式编程任务的方式。Lambda表达式本质上是匿名函数,允许我们传递代码块作为参数,或者将它们作为方法返回值。这篇教程针对...

    lambda-filter.html

    lambda-filter.html

    Python库 | mypy-boto3-lambda-1.17.104.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:mypy-boto3-lambda-1.17.104.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    PyPI 官网下载 | aws-solutions-constructs.aws-s3-lambda-1.94.0.tar.gz

    《PyPI中的aws-solutions-constructs.aws-s3-lambda-1.94.0:构建AWS S3与Lambda交互的高效解决方案》 在Python的世界里,PyPI(Python Package Index)是开发者们获取和分享软件包的重要平台。本文将深入探讨PyPI...

    PyPI 官网下载 | mypy-boto3-lambda-1.18.13.tar.gz

    在PyPI上,我们可以找到各种各样的Python库,其中之一就是我们关注的“mypy-boto3-lambda-1.18.13”。这个资源以`.tar.gz`格式提供,这是Linux和Unix系统中常见的压缩文件格式,它将所有文件打包并压缩,方便下载和...

    PyPI 官网下载 | mypy-boto3-lambda-1.10.39.2.tar.gz

    标题中的“PyPI官网下载 | mypy-boto3-lambda-1.10.39.2.tar.gz”表明这是一个从Python Package Index (PyPI) 官方网站上下载的软件包,名为“mypy-boto3-lambda”。这个软件包的版本是1.10.39.2,且其格式为tar.gz...

    api-lambda-app.iml

    api-lambda-app.iml

    Python库 | aws-cdk.aws-lambda-1.1.0.tar.gz

    标题中的"aws-cdk.aws-lambda-1.1.0.tar.gz"是一个针对Python的库,它是AWS Cloud Development Kit (CDK)的一部分,专门用于处理AWS Lambda服务。AWS CDK是一个开源软件开发框架,允许开发者使用熟悉的编程语言(如...

Global site tag (gtag.js) - Google Analytics