gpt4 book ai didi

javascript - 从for()移到map()-无法绕开它

转载 作者:行者123 更新时间:2023-12-03 02:28:20 25 4
gpt4 key购买 nike

想知道是否有人可以提供帮助-我想使用Array.mapArray.filter,但我陷入了我的for循环中,以为尽管阅读了教程等,但我似乎还是无法解决这个问题。

在这段代码中,我有一个Array对象,我想要:


将数组中的每个项目与其他项目进行比较,并确保obj[i] != obj[i]
对当前项目执行操作:检查item.target是否为null,比较distanceitem之间的item+1,以及itemitem+1 distance是否小于item 然后我想用 item.target替换 item.target


码:

for (var i = 0; i < 111; i++) {
var itm = {x:Math.random()*w, y:Math.random()*h, tgt:null};
dotArr.push(itm);
}
function findTarget(itemA, itemB){
var x1 = itemA.x;
var y1 = itemA.y;
var x2 = itemB.x;
var y2 = itemB.y;
var distance = Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 );
return distance;
}

for (var i = 0; i < dotArr.length; i++) {
let itm = dotArr[i];
for (var j = 0; j < dotArr.length; j++) {
if(itm != dotArr[j]){
let itm2 = this.dotArr[j];
if(itm.tgt==null){
itm.tgt = itm2;
}else{
let newDist = findTarget(itm, itm2);
let curDist = findTarget(itm, itm.tgt);
if(newDist<curDist){
itm.tgt = itm2;
}
}
}
}
}


我阅读的教程中的所有“将每个值乘以2”示例都是有意义的,但不能将其推断为我一直使用的方法。

预期结果:我有一堆粒子,它们遍历requestAnimationFrame()循环,检查每个循环的距离。每个粒子找到最接近的粒子并将其设置为“ tgt”(然后在其他代码中向其移动),但是会更新每个循环。

最佳答案

摘要

const distance = (a, b) => 
Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2))

const findClosest = (test, particles) => particles.reduce(
({val, dist}, particle) => {
const d = distance(test, particle)
return d < dist && d != 0 ? {val: particle, dist: d} : {val, dist}
},
{val: null, dist: Infinity}
).val

const addTargets = particles => particles.map(particle => {
particle.tgt = findClosest(particle, particles)
return particle
})


(由于数据结构的周期性,这很难在代码段中完成。JSON字符串化不适用于循环。)

出于正确原因更改样式

您说要从 for循环更改为 mapfilter等。等,但您不说为什么。确保出于适当原因执行此操作。我是函数式编程的坚决拥护者,并且通常会敦促我负责的初级开发人员进行此类更改。但我解释原因。

这是我的解释:


  “执行循环时,这样做是有原因的。如果您希望将值列表一一转换为另一值列表,则有一个内置的 map当您尝试检查应保留的代码时,您可以使用 filter来使代码更清晰,更简单。当您想在列表中找到带有某些属性,您有 find,这又又清楚又简单;如果您尝试合并元素,直到将它们简化为单个值,则可以使用 reduce,这很令人惊讶,更干净,更简单。
  
  “使用它们的原因是为了更好地表达代码的意图。您的意图永远不会是'从某个值开始一直递增某个计数器的值,并在满足某些条件时结束该计数器的值,然后执行一些例程每次迭代。”如果您可以使用能够更好地表达目标的工具,那么您的代码将更容易理解,因此请在代码中查找 mapfilterfindreduce有意义的地方。
  
  “并不是每个 for循环都适合这些模式之一,但是它们中的很大一部分都可以。替换适合的模式将使代码更容易理解,因此也更易于维护。”


我将在此继续说明永远不用担心击剑岗错误的优点,以及其中一些功能如何与更通用的类型一起使用,从而使此类代码的重用更加容易。但这是我与团队一起使用的基本要点。

您需要确定更改的原因,以及在您的情况下是否有意义。根据您的要求,确实有可能没有。

函数 mapfindfilter仅对列表中的单个项目起作用。 reduce用于一项和当前累计值。看来您的要求是在所有值之间成对单词。这可能意味着这些功能都不适合。

也许他们做到了。请继续阅读以了解如何解决此问题。

名称很重要

您包括一个名为 findTarget的函数。我会以为这样的函数会以某种方式找到目标。实际上,它全部用于计算两个项目之间的距离。

想象一下来到别人的代码并阅读使用 findTarget的代码。在阅读该功能之前,您将不知道它只是在计算距离。该代码似乎很奇怪。如果您仅将其命名为 distance,将很难理解。

同样,使用 item或缩写版 itm不会告诉读者任何有关这些内容的信息。 (更新:帖子的更改指出这些是“粒子”,因此我将在代码中使用它而不是 itm。)

避免棘手

findTarget / distance函数做了一些奇怪的事情,有些难以理解。它在计算中间修改计算变量: (x2-=x1)*x2(y2-=y1)*y2。虽然我可以看到效果相同,但是很容易编写一个非常清晰的距离函数而没有这个棘手的问题:

const distance = (a, b) => 
Math.sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y))


同样有很多变体。

const distance = (a, b) =>
Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2))


有一天,我们将能够做到

const distance = (a, b) => Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)


这些中的任何一个都会使代码更清晰。您也可以使用中间变量,例如 dx / dydeltaX / deltaY(如果这样更容易理解)。

仔细看您的要求

我花了很长时间查看您的代码才能确定您要尝试执行的操作。

如果您可以将需要的部分分解为命名函数,那么编写起来通常会更容易,而且通常别人也更容易理解(甚至几周后也可以理解)。

因此,如果我现在正确地理解了问题,那么您将拥有一列已定位的对象,并且对于每个对象,您都希望使用一个目标来更新它们,该目标是最接近它们的对象。听起来很像 map

鉴于此,我认为代码应类似于:

const addTargets = particles => particles.map(item => ({
x: item.x,
y: item.y,
tgt: findClosest(item, particles)
}))


现在,我还不知道 findClosest的工作方式,但是我希望只要我能写出来,它就能达到目标。

请注意,这个版本非常重视我对不变性的函数编程概念的信念。但这并不能完全满足您的要求,因为粒子的目标将是旧列表中的目标,而不是其自身列表中的目标。我个人可能会考虑更改数据结构以解决此问题。但是,让我们放宽该限制,而不是返回新项目,我们可以就地更新项目。

const addTargets = particles => particles.map(particle => {
particle.tgt = findClosest(particle, particles)
return particle
})


因此,请注意我们在这里所做的事情:我们正在将没有目标(或带有 null的目标)的项目列表变成具有目标的项目列表。但是,我们将其分为两个部分:一个将没有目标的元素转换为带有目标的元素。第二个为给定元素找到合适的目标。这样可以更清楚地捕获需求。

我们仍然必须弄清楚如何为元素找到合适的目标。概括地说,我们正在做的是获取一系列元素并将其变成单个元素。那是 reduce。 (这不是 find操作,因为它必须检查列表中的所有内容。)

让我们来写,然后:

const findClosest = (test, particles) => particles.reduce(
({val, dist}, particle) => {
const d = distance(test, particle)
return d < dist && d != 0 ? {val: particle, dist: d} : {val, dist}
},
{val: null, dist: Infinity}
).val


我们在此将距离用于双重目的。首先,当然,我们要看两个粒子之间的距离。但是第二,我们假设在相同精确位置的另一个粒子是相同粒子。如果这不正确,则必须对此进行一些更改。

在每次迭代中,我们都有一个具有 valdist属性的新对象。这总是代表我们到目前为止找到的最接近的粒子,以及它与当前粒子的距离。最后,我们只返回 val属性。 ( Infinity的原因是每个粒子都比该距离更近,因此我们不需要特定的逻辑来测试第一个粒子。)

结论

最后,我们能够使用 mapreduce。请注意,在此示例中,我们有两个可重用的辅助函数,但是每个函数仅使用一次。如果不需要重用它们,则可以将它们折叠为调用它们的函数。但我不建议这样做。此代码可读性强。折合起来,它们的表达力会降低。

关于javascript - 从for()移到map()-无法绕开它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48846058/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com