javascript क्या जावास्क्रिप्ट में सरणी आइटमों के आंशिक रूप से करने का एक बेहतर तरीका है?




arrays functional-programming (8)

मुझे आश्चर्य है कि किसी सरणी के आंशिक योगों के लिए बेहतर प्रदर्शन करने का एक बेहतर तरीका है।

किसी सरणी को x = [ 0, 1, 2, 3, 4, 5 ] , मैंने आइटमों के उप-सरणियों को उत्पन्न किया, और फिर प्रत्येक सरणी के योग की गणना की जो देता है:

[ 0, 1, 3, 6, 10, 15 ]

तो पूर्ण कोड है:

x.map((y,i)=>x.filter((t,j)=>j<=i))
 .map(ii=>ii.reduce((x,y)=>x+y,0))

मुझे आश्चर्य है कि अगर फ्लैट मैप या किसी अन्य सरणी विधि में एक समाधान होगा जिसमें प्रत्येक उपप्रकार के विस्तार की आवश्यकता नहीं है।


आप अंतिम राशि का ट्रैक रखने के लिए एक चर के साथ लूप के for उपयोग कर सकते हैं

let x = [ 0, 1, 2, 3, 4, 5 ]

let sum = (arr) => {
  let sum = 0
  let final = []
  for(let i=0; i<arr.length; i++){
    sum+= arr[i]
    final.push(sum)
  }
  return final
}

console.log(sum(x))

आप मानचित्र का उपयोग भी कर सकते हैं:

let x = [0, 1, 2, 3, 4, 5]

let sum = (arr) => {
  let sum = 0
  return arr.map(current => sum += current )
}

console.log(sum(x))


यदि आप बाहरी संचयकर्ता चर रखते हैं, तो सीधे मानचित्र का उपयोग करना संभव है:

const x = [ 0, 1, 2, 3, 4, 5 ];

let acc = 0;
const prefixSum = x.map(x => acc += x);

console.log(prefixSum);


एक पुनरावर्ती फ़ंक्शन का उपयोग करके यहां एक सरल उत्तर दिया गया है।

var array = [ 0, 1, 2, 3, 4, 5 ];

function sumArray(arrayToSum, index){
    if(index < arrayToSum.length-1){
        arrayToSum[index+1] = arrayToSum[index] + arrayToSum[index+1];
        return sumArray(arrayToSum, index+1);
  }else
    return arrayToSum;

}
sumArray(array, 0);

console.log(array);

आपको बस पिछले परिणाम के लिए हर कदम को वर्तमान मूल्य में जोड़ना होगा, ताकि आप एक सरल कमी का उपयोग कर सकें।

const array = [0, 1, 2, 3, 4, 5, 6];

const sums = array.reduce((acc,current,index) => {
  const prev = acc.length ? acc[index-1] : 0;
  acc.push(prev + current);
  return acc;
},[]);

console.log(sums.toString());


एक विकल्प एकल .reduce का उपयोग करना है जो कटा हुआ आंशिक सरणी को योग करने के लिए .reduce का उपयोग करता है:

const x = [0, 1, 2, 3, 4, 5];

const sum = (x, y) => x + y;
const partialSums = x.map((_, i, arr) => arr.slice(0, i + 1).reduce(sum));
console.log(partialSums);


नीचे, scan एक मैपिंग फ़ंक्शन f और एक प्रारंभिक संचायक r लेता है -

const scan = (f, r, [ x, ...xs ]) =>
  x === undefined
    ? [ r ]
    : [ r, ...scan (f, f (r, x), xs) ]
  
const add = (x, y) =>
  x + y

const print = (...vs) =>
  vs .forEach (v => console .log (v))

const data =
  [ 0, 1, 2, 3, 4, 5 ]
  
print
  ( scan (add, 0, data)
  , scan (Math.max, 3, data)
  , scan (add, 0, [])
  )

// [ 0, 0, 1, 3, 6, 10, 15 ]
// [ 3, 3, 3, 3, 3, 4, 5 ]
// [ 0 ]

यदि आपको एक प्रोग्राम की आवश्यकता है जो एक प्रारंभिक संचायक नहीं लेता है, तो इनपुट सरणी के पहले तत्व का उपयोग इसके बजाय किया जा सकता है। इस भिन्नता को scan1 कहा जाता है -

const scan = (f, r, [ x, ...xs ]) =>
  x === undefined
    ? [ r ]
    : [ r, ...scan (f, f (r, x), xs) ]
    
const scan1 = (f, [ x, ...xs ]) =>
  x === undefined
    ? []
    : scan (f, x, xs)

const add = (x, y) =>
  x + y
  
const print = (...vs) =>
  vs .forEach (v => console .log (v))

const data =
  [ 0, 1, 2, 3, 4, 5 ]

print
  ( scan1 (add, data)
  , scan1 (Math.max, data)
  , scan1 (Math.min, data)
  , scan1 (add, [])
  )
  
// [ 0, 1, 3, 6, 10, 15 ]
// [ 0, 1, 2, 3, 4, 5 ]
// [ 0, 0, 0, 0, 0, 0 ]
// []

प्रदर्शन अनुकूलन किए जा सकते हैं और स्टैक-ओवरफ्लो संकट का निवारण किया जा सकता है, यदि आवश्यक हो, तो सभी बिना कार्यात्मक बलिदान के -

const scan = (f, init, xs) =>
  loop
    ( ( r = []
      , a = init
      , i = 0
      ) =>
        i >= xs.length
          ? push (a, r)
          : recur
              ( push (a, r)
              , f (a, xs[i])
              , i + 1
              )
    )

अब इसे एक बड़े इनपुट के साथ चलाते हैं -

// BIG data!
const data =
  Array .from (Array (10000), (_, x) => x)

// fast and stack-safe
console .time ("scan")
const result = scan (add, 0, data)
console .timeEnd ("scan")
// scan: 8.07 ms

console .log (result)
// [ 0, 0, 1, 3, 6, 10, 15, ..., 49985001 ]

यह निम्नलिखित सामान्य कार्यात्मक प्रक्रियाओं पर निर्भर करता है -

const recur = (...values) =>
  ({ recur, values })

const loop = f =>
{ let r = f ()
  while (r && r.recur === recur)
    r = f (...r.values)
  return r
}

const push = (x, xs) =>
  ( xs .push (x)
  , xs
  )

अपने ब्राउज़र में परिणाम सत्यापित करने के लिए नीचे दिए गए स्निपेट का विस्तार करें -

const recur = (...values) =>
  ({ recur, values })

const loop = f =>
{ let r = f ()
  while (r && r.recur === recur)
    r = f (...r.values)
  return r
}

const push = (x, xs) =>
  ( xs .push (x)
  , xs
  )

const scan = (f, init, xs) =>
  loop
    ( ( r = []
      , a = init
      , i = 0
      ) =>
        i >= xs.length
          ? push (a, r)
          : recur
              ( push (a, r)
              , f (a, xs[i])
              , i + 1
              )
    )

const add = (x, y) =>
  x + y

const data =
  Array .from (Array (10000), (_, x) => x)
  
console .time ("scan")
const result = scan (add, 0, data)
console .timeEnd ("scan")

console .log (result)
// [ 0, 0, 1, 3, 6, 10, 15, ..., 49995000 ]


बहुत, एक कुल रनिंग रखकर:

function* partialSums(iterable) {
    let s = 0;

    for (const x of iterable) {
        s += x;
        yield s;
    }
}

const x = [0, 1, 2, 3, 4, 5];
console.log(Array.from(partialSums(x)).join(', '));

रैखिक समय, ऑनलाइन। (आप सीधे एक सरणी भी बना सकते हैं; नीचे विस्तार करें।)

const partialSums = arr => {
    let s = 0;
    return arr.map(x => s += x);
};

const x = [0, 1, 2, 3, 4, 5];
console.log(partialSums(x).join(', '));


फ्लैट का नक्शा आपके मामले में उपयोगी नहीं होगा, क्योंकि आप सूची के रूप में आने वाले अपने आंशिक परिणामों को समतल करने की कोशिश नहीं कर रहे हैं, लेकिन हम शायद एक ही बार में आपकी समस्या को हल करने का प्रयास कर सकते हैं:

[0, 1, 2, 3, 4, 5]
.reduce(
   ([arr, sum], el) => { // We pass along array and running sum
       const next = sum + el
       return [[...arr, next], next]
   },
   [[], 0] // We need to seed our reduce with empty array and accumulator for calculating running sum
)[0] // Array containing array and the last sum is returned, so we need to take only the first element

यह सरणी को केवल एक बार पुनरावृत्त करता है, इसलिए यह स्लाइस बनाने वाले समाधान की तुलना में थोड़ा अधिक प्रदर्शन करने वाला हो सकता है और फिर उन्हें समेट सकता है।

या array.push साथ एक संस्करण, जो समान सरणी का पुन: उपयोग करता है:

[0, 1, 2, 3, 4, 5]
.reduce(
   ([arr, sum], el) => { // We pass along array and running sum
       const next = sum + el
       arr.push(next)
       return [arr, next]
   },
   [[], 0] // We need to seed our reduce with empty array and accumulator for calculating running sum
)[0] 




prefix-sum