Просто для удовольствия я играл с представлением поплавков, следуя определениям из стандарта C99, и я написал код ниже.
Код печатает двоичное представление поплавков в 3 отдельных группах
SIGN EXPONENT FRACTION
, и после этого он печатает сумму, которая при суммировании с достаточной точностью покажет значение, которое действительно существует в аппаратном обеспечении.
Поэтому, когда вы пишете float x = 999...
компилятор преобразует это число в битовое представление, напечатанное функцией xx
, так что сумма, напечатанная функцией yy
, будет равна заданному числу.
В действительности эта сумма является только приближение. Для числа 999,999,999 компилятор будет вставлять в бит представление float число 1,000,000,000
После кода я присоединяю консольный сеанс, в котором я вычисляю сумму терминов для обеих констант (минус PI и 999999999) который действительно существует в аппаратном обеспечении, вставленном там компилятором.
#include <stdio.h>
#include <limits.h>
void
xx(float *x)
{
unsigned char i = sizeof(*x)*CHAR_BIT-1;
do {
switch (i) {
case 31:
printf("sign:");
break;
case 30:
printf("exponent:");
break;
case 23:
printf("fraction:");
break;
}
char b=(*(unsigned long long*)x&((unsigned long long)1<<i))!=0;
printf("%d ", b);
} while (i--);
printf("\n");
}
void
yy(float a)
{
int sign=!(*(unsigned long long*)&a&((unsigned long long)1<<31));
int fraction = ((1<<23)-1)&(*(int*)&a);
int exponent = (255&((*(int*)&a)>>23))-127;
printf(sign?"positive" " ( 1+":"negative" " ( 1+");
unsigned int i = 1<<22;
unsigned int j = 1;
do {
char b=(fraction&i)!=0;
b&&(printf("1/(%d) %c", 1<<j, (fraction&(i-1))?'+':')' ), 0);
} while (j++, i>>=1);
printf("*2^%d", exponent);
printf("\n");
}
void
main()
{
float x=-3.14;
float y=999999999;
printf("%lu\n", sizeof(x));
xx(&x);
xx(&y);
yy(x);
yy(y);
}
Вот сеанс консоли, в котором я вычисляю реальное значение float, которое существует в аппаратном обеспечении. Я использовал bc
для печати суммы терминов, выводимых основной программой. Можно вставить эту сумму в python repl
или что-то подобное.
-- .../terra1/stub
@ qemacs f.c
-- .../terra1/stub
@ gcc f.c
-- .../terra1/stub
@ ./a.out
sign:1 exponent:1 0 0 0 0 0 0 fraction:0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1
sign:0 exponent:1 0 0 1 1 1 0 fraction:0 1 1 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
negative ( 1+1/(2) +1/(16) +1/(256) +1/(512) +1/(1024) +1/(2048) +1/(8192) +1/(32768) +1/(65536) +1/(131072) +1/(4194304) +1/(8388608) )*2^1
positive ( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
-- .../terra1/stub
@ bc
scale=15
( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
999999999.999999446351872
Вот и все. Фактически значение 999999999
999999999.999999446351872
Вы также можете проверить с помощью bc
, что -3.14 также возмущено. Не забудьте установить коэффициент scale
в bc
.
Отображаемая сумма - это то, что внутри аппаратного обеспечения. Значение, которое вы получаете, вычисляя его, зависит от установленного вами масштаба. Я установил коэффициент scale
равным 15. Математически, с бесконечной точностью, кажется, что это 1 000 000 000.
Если вы хотите версию на npm, array-move является ближайшим к этому ответу, хотя это не та же реализация. Дополнительную информацию см. В разделе использования. Предыдущая версия этого ответа (модифицированного Array.prototype.move) может быть найдена на npm в array.prototype.move .
У меня был довольно хороший успех с эта функция:
function array_move(arr, old_index, new_index) {
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing
};
// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1));
Обратите внимание, что последний return
просто предназначен для тестирования: splice
выполняет операции над массивом на месте, поэтому возврат не требуется. В дополнение, этот move
является операцией на месте. Если вы хотите избежать этого и вернуть копию, используйте slice
.
Выполняя код:
new_index
больше длины массива, мы хотим (я полагаю) правильно разместить массив с новыми undefined
s. Этот небольшой фрагмент обрабатывает это, нажимая undefined
на массив до тех пор, пока мы не получим правильную длину. arr.splice(old_index, 1)[0]
мы соединим старый элемент. splice
возвращает элемент, который был сращирован, но находится в массиве. В нашем примере выше это было [1]
. Итак, мы берем первый индекс этого массива, чтобы получить там исходный 1
. splice
для вставки этого элемента в место new_index. Поскольку мы добавили массив выше, если new_index > arr.length
, он, вероятно, появится в нужном месте, если только они не сделали что-то странное, как передать отрицательное число. Более благоприятная версия для учета отрицательных индексов:
function array_move(arr, old_index, new_index) {
while (old_index < 0) {
old_index += arr.length;
}
while (new_index < 0) {
new_index += arr.length;
}
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing purposes
};
// returns [1, 3, 2]
console.log(array_move([1, 2, 3], -1, -2));
Что должно учитывать такие вещи, как array_move([1, 2, 3], -1, -2)
правильно (переместите последний элемент во второе и последнее место). Результат для этого должен быть [1, 3, 2]
.
В любом случае, в вашем исходном вопросе вы сделаете array_move(arr, 0, 2)
для a
после c
. Для d
до b
вы сделали бы array_move(arr, 3, 1)
.
Это основано на решении Рейда. За исключением:
Array
. undefined
, оно просто перемещается Функция:
function move(array, oldIndex, newIndex) {
if (newIndex >= array.length) {
newIndex = array.length - 1;
}
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
return array;
}
Единичные тесты:
describe('ArrayHelper', function () {
it('Move right', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 0, 1);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
})
it('Move left', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, 0);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
});
it('Move out of bounds to the left', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, -2);
assert.equal(array[0], 2);
assert.equal(array[1], 1);
assert.equal(array[2], 3);
});
it('Move out of bounds to the right', function () {
let array = [1, 2, 3];
arrayHelper.move(array, 1, 4);
assert.equal(array[0], 1);
assert.equal(array[1], 3);
assert.equal(array[2], 2);
});
});
В итоге я объединил два из них, чтобы немного поработать, двигаясь как на большие, так и на большие расстояния. Я получаю довольно последовательные результаты, но это, вероятно, может быть немного изменено кем-то умнее меня, чтобы работать по-разному для разных размеров и т. Д.
Использование некоторых других методов при перемещении объектов на небольшие расстояния было значительно быстрее (x10), чем использование сплайсинга. Это может меняться в зависимости от длины массива, но это верно для больших массивов.
function ArrayMove(array, from, to) {
if ( Math.abs(from - to) > 60) {
array.splice(to, 0, array.splice(from, 1)[0]);
} else {
// works better when we are not moving things very far
var target = array[from];
var inc = (to - from) / Math.abs(to - from);
var current = from;
for (; current != to; current += inc) {
array[current] = array[current + inc];
}
array[to] = target;
}
}
Получил эту идею от @Reid, чтобы толкать что-то вместо места, которое должно быть перемещено, чтобы сохранить размер массива постоянным. Это упрощает вычисления. Кроме того, нажатие пустого объекта имеет дополнительные преимущества в том, что его можно искать по отдельности позже. Это работает, потому что два объекта не равны, пока они не обращаются к одному и тому же объекту.
({}) == ({}); // false
Итак, вот функция, которая берет в исходном массиве и указателях источника, целевых. Вы можете добавить его в Array.prototype, если необходимо.
function moveObjectAtIndex(array, sourceIndex, destIndex) {
var placeholder = {};
// remove the object from its initial position and
// plant the placeholder object in its place to
// keep the array length constant
var objectToMove = array.splice(sourceIndex, 1, placeholder)[0];
// place the object in the desired position
array.splice(destIndex, 0, objectToMove);
// take out the temporary object
array.splice(array.indexOf(placeholder), 1);
}
Array.prototype.moveUp = function (value, by) {
var index = this.indexOf(value),
newPos = index - (by || 1);
if (index === -1)
throw new Error("Element not found in array");
if (newPos < 0)
newPos = 0;
this.splice(index, 1);
this.splice(newPos, 0, value);
};
Array.prototype.moveDown = function (value, by) {
var index = this.indexOf(value),
newPos = index + (by || 1);
if (index === -1)
throw new Error("Element not found in array");
if (newPos >= this.length)
newPos = this.length;
this.splice(index, 1);
this.splice(newPos, 0, value);
};
var arr = ['banana', 'curyWurst', 'pc', 'remembaHaruMembaru'];
alert('withiout changes= '+arr[0]+' ||| '+arr[1]+' ||| '+arr[2]+' ||| '+arr[3]);
arr.moveDown(arr[2]);
alert('third word moved down= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);
arr.moveUp(arr[2]);
alert('third word moved up= '+arr[0] + ' ||| ' + arr[1] + ' ||| ' + arr[2] + ' ||| ' + arr[3]);
Я реализовал неизменяемое решение ECMAScript 6
, основанное на ответе @Merc
здесь:
const moveItemInArrayFromIndexToIndex = (array, fromIndex, toIndex) => {
const newArray = [...array];
if (fromIndex === toIndex) return newArray;
const target = newArray[fromIndex];
const inc = toIndex < fromIndex ? -1 : 1;
for (let i = fromIndex; i !== toIndex; i += inc) {
newArray[i] = newArray[i + inc];
}
newArray[toIndex] = target;
return newArray;
};
Имена переменных могут быть сокращены, просто используются длинные, чтобы код может объяснить себя.
Во многих местах указывается ( добавление пользовательских функций в Array.prototype ), играя с прототипом Array, может быть плохой идеей, в любом случае я сочетал лучшее из разных сообщений, я пришел с этим, используя современный Javascript:
Object.defineProperty(Array.prototype, 'immutableMove', {
enumerable: false,
value: function (old_index, new_index) {
var copy = Object.assign([], this)
if (new_index >= copy.length) {
var k = new_index - copy.length;
while ((k--) + 1) { copy.push(undefined); }
}
copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
return copy
}
});
//how to use it
myArray=[0, 1, 2, 3, 4];
myArray=myArray.immutableMove(2, 4);
console.log(myArray);
//result: 0, 1, 3, 4, 2
Надежда может быть полезна любому
Я использовал хороший ответ @Reid , но боролся с перемещением элемента из конца массива на один шаг дальше - в начало (как в цикле). Например. ['a', 'b', 'c'] должны стать ['c', 'a', 'b'], вызвав .move (2,3)
. Я достиг этого, изменив case for new_index> = this.length.
Array.prototype.move = function (old_index, new_index) {
console.log(old_index + " " + new_index);
while (old_index < 0) {
old_index += this.length;
}
while (new_index < 0) {
new_index += this.length;
}
if (new_index >= this.length) {
new_index = new_index % this.length;
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this; // for testing purposes
};
В дополнение к отличный ответ Рида (и потому, что я не могу прокомментировать); Вы можете использовать modulo, чтобы сделать как отрицательные индексы, так и слишком большие индексы «перевернуться»:
function array_move(arr, old_index, new_index) {
new_index =((new_index % arr.length) + arr.length) % arr.length;
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr; // for testing
}
// returns [2, 1, 3]
console.log(array_move([1, 2, 3], 0, 1));
Вот один лайнер, который я нашел на JSPerf ....
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
, который является замечательным для чтения, но если вы хотите, чтобы производительность (в небольших наборах данных) пыталась ...
Array.prototype.move2 = function(pos1, pos2) {
// local variables
var i, tmp;
// cast input parameters to integers
pos1 = parseInt(pos1, 10);
pos2 = parseInt(pos2, 10);
// if positions are different and inside array
if (pos1 !== pos2 && 0 <= pos1 && pos1 <= this.length && 0 <= pos2 && pos2 <= this.length) {
// save element from position 1
tmp = this[pos1];
// move element down and shift other elements up
if (pos1 < pos2) {
for (i = pos1; i < pos2; i++) {
this[i] = this[i + 1];
}
}
// move element up and shift other elements down
else {
for (i = pos1; i > pos2; i--) {
this[i] = this[i - 1];
}
}
// put element from position 1 to destination
this[pos2] = tmp;
}
}
Я не могу взять кредит, все должно пойти на Richard Scarrott . Он использует метод сплайсинга для меньших наборов данных в этом тесте производительности . Тем не менее он значительно медленнее на больших наборах данных , как указывает Дарвейн .
Один из подходов состоял в том, чтобы создать новый массив с фигурами в том порядке, в котором вы хотите, используя метод среза.
Пример
var arr = [ 'a', 'b', 'c', 'd', 'e'];
var arr2 = arr.slice(0,1).concat( ['d'] ).concat( arr.slice(2,4) ).concat( arr.slice(4) );
Мне нужен был неизменный метод перемещения (тот, который не менял исходный массив), поэтому я применил принятый ответ @ Reid, чтобы просто использовать Object.assign для создания копии массива перед выполнением сращивания.
Array.prototype.immutableMove = function (old_index, new_index) {
var copy = Object.assign([], this);
if (new_index >= copy.length) {
var k = new_index - copy.length;
while ((k--) + 1) {
copy.push(undefined);
}
}
copy.splice(new_index, 0, copy.splice(old_index, 1)[0]);
return copy;
};
Перемещает элементы внутри массива, возвращая массив, содержащий перемещенные элементы.
array.move(index, howMany, toIndex);
index: Индекс, по которому нужно перемещать элементы. Если отрицательный, индекс начнется с конца.
howMany: количество элементов, которые нужно переместить из индекса.
toIndex: указатель массива, в который нужно поместить перемещенные элементы. Если отрицательный, toIndex начнется с конца.
array = ["a", "b", "c", "d", "e", "f", "g"];
array.move(3, 2, 1); // returns ["d","e"]
array; // returns ["a", "d", "e", "b", "c", "f", "g"]
Array.prototype.move || Object.defineProperty(Array.prototype, "move", {
value: function (index, howMany, toIndex) {
var
array = this,
index = parseInt(index) || 0,
index = index < 0 ? array.length + index : index,
toIndex = parseInt(toIndex) || 0,
toIndex = toIndex < 0 ? array.length + toIndex : toIndex,
toIndex = toIndex <= index ? toIndex : toIndex <= index + howMany ? index : toIndex - howMany,
moved;
array.splice.apply(array, [toIndex, 0].concat(moved = array.splice(index, howMany)));
return moved;
}
});
Метод splice
из Array
может помочь: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice
Просто имейте в виду, что это может быть относительно дорого, поскольку оно должно активно переиндексировать массив.
My 2c. Легко читается, он работает, он работает быстро, он не создает новые массивы.
function move(array, from, to) {
if( to === from ) return array;
var target = array[from];
var increment = to < from ? -1 : 1;
for(var k = from; k != to; k += increment){
array[k] = array[k + increment];
}
array[to] = target;
return array;
}
Вы можете реализовать базовое исчисление и создать универсальную функцию для перемещения элемента массива из одной позиции в другую.
Для JavaScript это выглядит так:
function magicFunction (targetArray, indexFrom, indexTo) {
targetElement = targetArray[indexFrom];
magicIncrement = (indexTo - indexFrom) / Math.abs (indexTo - indexFrom);
for (Element = indexFrom; Element != indexTo; Element += magicIncrement){
targetArray[Element] = targetArray[Element + magicIncrement];
}
targetArray[indexTo] = targetElement;
}
Check
http://www.gloommatter.com/DDesign/programming/moving-any-array-elements-universal-функция. HTML
Вот мое единственное решение ES6 Liner с необязательным параметром on
.
if (typeof Array.prototype.move === "undefined") {
Array.prototype.move = function(from, to, on = 1) {
this.splice(to, 0, ...this.splice(from, on))
}
}
Адаптация первого решения, предложенного digiguru
Параметр on
- это номер элемента, начиная с from
, который вы хотите переместить.
Мне нравится этот путь. Он работает, он быстрый и элегантный.
function arraymove(arr, fromIndex, toIndex) {
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}
Примечание: всегда помните, чтобы проверить границы массива.
Метод splice () добавляет / удаляет элементы в / из массива и возвращает удаленные элементы.
Примечание. Этот метод изменяет исходный массив. / w3schools /
blockquote>Array.prototype.move = function(from,to){ this.splice(to,0,this.splice(from,1)[0]); return this; }; var arr = [ 'a', 'b', 'c', 'd', 'e']; arr.move(3,1);//["a", "d", "b", "c", "e"] var arr = [ 'a', 'b', 'c', 'd', 'e']; arr.move(0,2);//["b", "c", "a", "d", "e"]
, поскольку функция chainable тоже работает:
alert(arr.move(0,2).join(','));