Это, безусловно, может.
Вы могли бы начать с этого:
classify(vector, distance) {
let min = Infinity;
let index = 0;
for (let i = 0; i < this.centroids.length; i++) {
const dist = distance(vector, this.centroids[i]);
if (dist < min) {
min = dist;
index = i;
}
}
return index;
}
Почему это функция-член? Разве чистая функция const classify = (centroids, vector, distance) => {...}
не будет чище?
Тогда для реализации давайте немного изменим сигнатуру distance
. Если мы добавим const distance = (vector) => (centroid) => {...}
, то сможем написать
const classify = (centroids, vector, distance) =>
minIndex (centroids .map (distance (vector)))
И если этот distance
API находится вне нашего контроля, это не намного сложнее:
const classify = (centroids, vector, distance) =>
minIndex (centroids .map (centroid => distance (vector, centroid)))
Предоставлено мы еще не написали minIndex
, но мы уже разбили проблему, чтобы использовать более содержательную абстракцию. И minIndex
не сложно написать. Вы можете сделать это обязательно, как это делала оригинальная функция classify
, или с помощью чего-то вроде этого:
const minIndex = (xs) => xs.indexOf (Math.min (...xs))
Обратите внимание, что distance
здесь слегка вводит в заблуждение. Я должен был прочитать это более внимательно, потому что я предполагал, что такое имя будет представлять ..., ну, на некотором расстоянии. Вместо этого это функция, используемая для расчета расстояния. Возможно, имя metric
или что-то вроде distanceFunction
, distanceFn
или distanceImpl
будет более очевидным.
Теперь давайте перейдем к этому биту:
const newCentroid = new Array(centroid.length);
for (let g = 0; g < centroid.length; g++) {
let sum = 0;
for (let i = 0; i < assigned.length; i++) {
sum += assigned[i][g];
}
newCentroid[g] = sum / assigned.length;
if (newCentroid[g] !== centroid[g]) {
movement = true;
}
}
Этот код имеет две обязанности: создание массива newCentroid
и обновление значения movement
, если какое-либо значение изменилось.
Давайте разделим эти два.
Сначала создадим новый центроид. Мы можем очистить вложенную for
-l oop примерно так:
const makeNewCentroid = (centroid, assigned) =>
centroid .map ((c, g) => mean (assigned .map ((a) => a[g])))
Это зависит от функции mean
, которую мы напишем вместе с требуемой функцией sum
как это:
const sum = (ns) => ns .reduce ((t, n) => t + n, 0)
const mean = xs => sum (xs) / xs.length
Тогда нам нужно обновить movement
. Мы можем сделать это легко, основываясь на centroids
и newCentroids
:
movement = centroids.some((c, i) => c !== newCentroids[i])
Очевидно, вы можете продолжать в том же духе. Каждый for
1 oop должен иметь фундаментальное назначение. Найдите эту цель и посмотрите, может ли один из Array.prototype
методов улучшить express. Во втором разделе, с которым мы работали выше, мы нашли две цели и просто разбили их на два отдельных блока.
Это должно дать вам хорошее начало для повышения функциональности. Там нет волхвов c пуля. Но если вы мыслите в терминах чистых функций неизменяемых данных и сильного разделения интересов, вы обычно можете двигаться в функциональном направлении.