【C#杂谈】当Grasshopper中的C# Scriptable电池遇到LinQ,抛弃Python的理由又多了一条

本文主要是介绍【C#杂谈】当Grasshopper中的C# Scriptable电池遇到LinQ,抛弃Python的理由又多了一条,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

LinQ的全称Language Integrated Query,是一种将数据库理念,比如常用的查找(Select)、排序(Order By)、条件过滤(Where)等延伸到一般编程中的技术路径。

Grasshopper的日常使用其中也包含了大量数据传递/使用的过程,比如按一定规律生成一系列点、将一系列直线按某种条件过滤掉等。虽然Grasshopper自带的运算器可以实现这些功能,但是有时候需要拖入大量的电池来实现一个简单的过滤逻辑。比如要从一堆点里选出X坐标大于1且小于5的点,如果只使用Grasshopper自带的电池,就需要用到下列的电池组

在这里插入图片描述

这时候Python就能派上大用场了:使用Python电池,一行就搞定了

	a = [pt for pt in x if pt.X > 1 and pt.X < 5]

在这里插入图片描述
虽然,C# Scriptable电池也能做得到这一点…… 但是这代码量可能就得多一点了

	var list = new List<Point3d>();foreach (var item in x)if (item.X > 1 && item.X < 5)list.Add(item);A = list;

在这里插入图片描述
所以,很多时候,对于这类纯粹的数据操作(不涉及到几何运算、复杂对象操作)而言,Python往往获得了更多的人的青睐 —— 既能少写代码,又能让代码看起来更酷,列表生成式的逻辑表达也十分清楚。

反观C#的代码,foreach循环套一个if条件判断,代码量比Python多,看起来也平平无奇,没有亮点嘛。


但是,C#真的如此吗?


加上System.Linq命名空间,C#也能变得很酷:

using System.Linq;
// ... ...A = x.Where(pt => pt.X > 1 && pt.X < 5);

在这里插入图片描述

下面是过滤处理100,000个数据点时,各个代码的效率对比,作为对照组,下面两个电池不做过滤,直接原路输出数据,作为对照。

在这里插入图片描述
Python的对照组居然比过滤后需要的时间更多?这是因为Python电池在运行时需要把来自Grasshopper内置的对象转换成为Python对象之后,才能在Python电池里处理,然后在输出时又要转回Grasshopper的对象,这需要对象被转换两次。因为过滤后的点的数量会变少,所以转换对象数量变少了,电池的运行时间就会变短。

Grasshopper本身就是C#写的,所以C# Scriptable电池在使用过程中不存在类似于Python一样的对象转换的过程,因此直接将点的列表原封不动地传出所需要的时间更少。



using System.Linq;

哪里能用Linq?

System.Linq命名空间里包含了对IEnumerable接口的一系列的扩展方法,只要对象遵循IEnumerable接口(你能想到的所有的存储多个对象的存储结构都会遵循这个接口,比如ListArrayDictionaryStackQueue、……),都能直接使用Linq。

换句话说,无论哪都能用Linq,只要你想。

匿名函数

匿名函数又叫Lambda函数,语法为

(参数类型 参数名称, ...) => { 函数体 };

参数由逗号分隔,函数的返回值类型由函数体内的返回值确定,如果没有返回值则返回类型是void

lambda函数是Linq使用的精髓,它能让Linq语法更简练、更有逻辑。比如前面中用来过滤的.Where()函数中就是用了过滤条件的lambda函数

	pt => pt.X > 1 && pt.X < 5

写全其完整版本就是

	(Point3d pt) => {return (pt.X > 1 && pt.X < 5);}

也就是这个函数是“输入参数是Point3d类型,返回值是bool类型”的一个匿名函数。

因为只有一个参数,且参数类型由.Where()前的IEnumerable中包含的对象类型确定了(此处,x的类型是List<Point3d>,因此可以确定函数的参数类型为Point3d),所以可以省略( )。因为函数体只有一行表达式,所以可以省略{ }

x.Where()执行时,会将来自列表x的所有的每一个对象作为输入参数,执行一遍这个匿名函数,如果返回值是true,则保留该对象,返回值是false则滤掉该对象,最终只会留下

“将对象作为输入放入Lambda函数中,返回值是true

的这一批对象。

x.Where( pt => pt.X > 1 && pt.X < 5 ) 差不多等价于

bool Lambda_Func(Point3d pt) 
{return (pt.X > 1 && pt.X < 5)
}foreach (var item in x)
{if (Lambda_Func(item))// 接收这个对象else // 滤掉这个对象
}		

当然,也可以不用匿名函数,因为.Where()接收的是一个函数参数,也可以这样:

bool FilterFunction(Point3d pt) 
{return (pt.X > 1 && pt.X < 5)
}var filteredPoints = x.Where( FilterFunction );

把我们的过滤条件变成一个有名字(FilterFunction)的函数,既然有名字就不能叫“匿名”函数了,不过作用还是一样的。

由于大概率这类函数仅仅会用到这一次(过滤完数据之后就不会用到了),所以很多时候我们并不会费劲地给这个函数起个名字,直接使用匿名函数构造一个只用一次的函数就可以了。而且还能让逻辑变得更清晰。

Select

使用Select()可以将IEnumerable中的对象作为参数,转换整个IEnumerable的对象类型。简单点来说,这是一个单行foreach

看下列的代码,直接复制到 https://dotnetfiddle.net/ 去执行(这是一个在线编译器,可以方便测试小代码)

using System;
using System.Collections.Generic;
using System.Linq;public class Program
{public static void Main(){var i = new List<int>{ 1, 2, 3, 4, 5, 6 };var j = i.Select(num => Math.Sqrt(num));foreach (var jtem in j) Console.WriteLine(jtem.ToString());}
}

可以看到,通过一个Select命令将i中的整数通过匿名函数num => Math.Sqrt(num)“投影”到了j中去,相当于i中每一个对象都被扔到匿名函数中执行了一遍,将结果放到了j中去。有些时候这种操作叫做管道。这是很典型的函数式编程的思想。

为什么这种操作叫做SELECT呢,这是因为实践当中它一般是不会涉及计算的,一半它是用来批量获取一个类中的某个特定属性,比如所有点的X坐标:

List<Point3d> pointList = new List<Point3d>(); 
// ... ... ...
// ... ... ... 填满这个点的列表
// .... . ...
var pointX = pointList.Select( p => p.X );

这样就十分的简单、高效。

Where

最开始的例子就提到了使用Where的最常用的情况了。只需给出一个“返回值是bool”的匿名函数,就能把原数据中“匿名函数返回为true”的对象选出来。

在 https://dotnetfiddle.net/ 中试试看这段代码:【选择所有的偶数】

using System;
using System.Collections.Generic;
using System.Linq;public class Program
{public static void Main(){var i = new List<int>{ 1, 2, 3, 4, 5, 6 };var j = i.Where( n => n%2 == 0 );foreach (var jtem in j) Console.WriteLine(jtem.ToString());}
}

OrderBy

以某种方式排序,它用到的匿名函数所需要的返回值是一个“可比较”的类型,比如intdouble等。一个直观的例子,比如我们想要对一堆点排序,但是点由X、Y、Z三个坐标值构成,我们要以什么排序呢?那这里匿名函数就要返回这个 “排序的依据”

比如我们这以Y坐标为依据来排列点的顺序:

在这里插入图片描述
电池中的代码仅有一行:

	A = x.OrderBy(pt => pt.Y); // 匿名函数返回值为 Y 坐标,用于作为排序依据

返回啥就会按照啥来排序。

OrderByDescending

OrderBy一样,但是是从大到小排。

倒序排列。

ToList 和 ToArray

前面提到的所有的Linq语法的结果都是一个IEnumerable对象,不支持通过序号去访问其中的对象,比如x[10]。但是我们很多时候需要这样的功能,所以怎么把Linq的语法结果换成我们常用的List或者Array呢,就是使用ToList()ToArray()

它们不需要输入任何参数。

var i = new List<int>{ 1, 2, 3, 4, 5, 6 };
var j = i.Where( n => n%2 == 0 );  // j 是一个 IEnumerable<int> 类型List<double> doubleList = j.ToList();   // 转换成为 List<int>
// ... ...

将它们连起来

Linq语法最有优势的一点就是它能连起来使用,让整个数据流逻辑变得清晰无比:

比如在Grasshopper从一堆点里:

  • 挑出来X在1到5之间
  • Y在2到8之间
  • 并以这些点为圆心画一个圆,其半径为它自己的X坐标与Y坐标的差值
  • 将这些圆以圆心的Y坐标排序 (小到大)
  • 将这些圆放到List中去

当我想到要拖多少个电池的时候,我已经不想说话了,那将会是一个灾难。

但是用Linq:

List<Circle> circleList = x.Where(pt => pt.X > 1 && pt.X < 5).Where(pt => pt.Y > 2 && pt.Y < 8).Select(pt => new Circle(pt, Math.Abs(pt.X - pt.Y))).OrderBy(c => c.Center.Y).ToList();

在这里插入图片描述
妙啊!

自从用熟了Linq,在Grasshopper中Python电池已经吃灰好些日子了……

这篇关于【C#杂谈】当Grasshopper中的C# Scriptable电池遇到LinQ,抛弃Python的理由又多了一条的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/392754

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

总有一条路,我们很迷茫

十年前,我家还处于一个贫穷落后的小山村,周围的人会根据我父母的收入来对待我,而十年后的今天,我家的那座小山村医成为重点开发的地区,一夜之间我家成了所谓的土豪,周围的人依然根据我家的收入对待我。现实,什么是现实?这就是现实。从那一刻,我开始明白要想得到别人的尊重,首先你得有别人尊重的实力。 所以,这么多年来不管自己过得多累,走得多艰辛,我都会一直坚持。在人生前进的道路,我们总会经历风雨,难免感到迷

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip