给定Polygon
二维信息,point
是一个行向量,其他变量是标量,这是您的新函数的第一个版本(向下滚动以查看有很多方法可以给这只猫剥皮):
function [result] = newHitTest (point,Polygon,r,tol,stepSize)
result = 0;
linDiff = Polygon-repmat(point,size(Polygon,1),1);
testLogicals = sqrt( sum( ( linDiff ).^2 ,2 )) < tol*r;
if any(testLogicals); result = circleTest (point,Polygon,r,tol,stepSize); end
Matlab 中向量化的思考过程涉及尝试使用单个命令对尽可能多的数据进行操作。大多数基本的内置 Matlab 函数在多维数据上运行非常有效。使用for
循环与此相反,因为您将数据分解为更小的部分进行处理,每个部分都必须单独解释。通过使用for
循环进行数据分解,您可能会失去一些与 Matlab 内置函数背后高度优化的代码相关的巨大性能优势。
在您的示例中首先要考虑的是主循环中的条件中断。您不能脱离矢量化过程。相反,计算所有可能性,为数据的每一行创建一个结果数组,然后使用any
关键字查看是否有任何行表明circleTest
应该调用该函数。
注意:在 Matlab 中有效地有条件地打破计算并不容易。但是,由于您只是在循环中计算欧几里德距离的一种形式,因此通过使用矢量化版本并计算所有可能性,您可能会看到性能提升。如果循环中的计算成本更高,输入数据很大,并且您想在遇到特定条件时立即中断,那么使用编译语言制作的 matlab 扩展可能比矢量化版本快得多,其中您可能正在执行不必要的计算。但是,这是假设您知道如何使用可编译为本机代码的语言编写与 Matlab 内置程序性能相匹配的代码。
回到主题...
首先要做的是获取和行向量linDiff
之间的线性差异(在代码示例中)。要以矢量化方式执行此操作,两个变量的维度必须相同。实现此目的的一种方法是使用复制每一行以使其大小与. 但是,通常是 repmat 的更好替代方案(如最近的 SO 问题中所述),使代码......Polygon
point
repmat
point
Polygon
bsxfun
function [result] = newHitTest (point,Polygon,r,tol,stepSize)
result = 0;
linDiff = bsxfun(@minus, Polygon, point);
testLogicals = sqrt( sum( ( linDiff ).^2 ,2 )) < tol*r;
if any(testLogicals); result = circleTest (point,Polygon,r,tol,stepSize); end
我通过在第二个轴上求和将您的值滚动d
到一列中d
(注意从命令中删除数组索引Polygon
并,2
在sum
命令中添加)。然后我更进一步,通过testLogicals
计算距离度量来评估逻辑数组。您很快就会发现重向量化的一个缺点是它会使不熟悉 Matlab 的人的代码可读性降低,但性能提升是值得的。评论非常有必要。
现在,如果您想完全发疯,您可能会争辩说测试函数现在非常简单,以至于它需要使用“匿名函数”或“lambda”而不是完整的函数定义。是否值得做的测试circleTest
也不需要stepSize
参数,这可能是使用匿名函数的另一个原因。您可以将您的测试滚动到一个匿名函数中,然后circleTest
在您的调用脚本中使用,从而使代码在一定程度上自我记录。. .
doCircleTest = @(point,Polygon,r,tol) any(sqrt( sum( bsxfun(@minus, Polygon, point).^2, 2 )) < tol*r);
if doCircleTest(point,Polygon,r,tol)
result = circleTest (point,Polygon,r,tol,stepSize);
else
result = 0;
end
现在一切都被矢量化了,函数句柄的使用给了我另一个想法。. .
如果您计划在代码中的多个点执行此操作,那么if
语句的重复会变得有点难看。为了保持干燥,将带有条件函数的测试放在一个函数中似乎是明智的,就像您在原始帖子中所做的那样。但是,该函数的效用将非常狭窄——它只会测试该circleTest
函数是否应该执行,然后在需要时执行它。
现在想象一下,过了一会儿,你有一些其他的条件函数,就像circleTest
,它们自己的等价物doCircleTest
。也许重用条件切换代码会很好。为此,请制作一个类似于原始函数的函数,该函数采用默认值、计算成本低的测试函数的布尔结果以及昂贵的条件函数的函数句柄及其相关参数......
function result = conditionalFun( default, cheapFunResult, expensiveFun, varargin )
if cheapFunResult
result = expensiveFun(varargin{:});
else
result = default;
end
end %//of function
您可以使用以下内容从主脚本中调用此函数。. .
result = conditionalFun(0, doCircleTest(point,Polygon,r,tol), @circleTest, point,Polygon,r,tol,stepSize);
...它的美妙之处在于您可以使用任何测试、默认值和昂贵的功能。这个简单的例子可能有点矫枉过正,但当我提出使用函数句柄的想法时,这就是我的想法。