ติดตามจำนวนครั้งที่เรียกใช้ฟังก์ชันเรียกซ้ำ


62

 function singleDigit(num) {
      let counter = 0
      let number = [...num + ''].map(Number).reduce((x, y) => {return x * y})

      if(number <= 9){
          console.log(number)
      }else{
          console.log(number)
          return singleDigit(number), counter += 1
      }
   }
singleDigit(39)

โค้ดด้านบนใช้จำนวนเต็มและย่อให้เป็นตัวเลขเดียวโดยคูณด้วยตัวเลขของตัวเอง

ตัวอย่างคือ 39

3 x 9 = 27.
2 x 7 = 14.
1 x 4 = 4.

คอนโซลจะเข้าสู่ระบบ:

27 
14 
4

ฉันจะติดตามได้อย่างไรว่าฟังก์ชั่นวนซ้ำเรียกว่า 3 ครั้ง?

ฉันลองเพิ่มตัวนับ แต่ไม่สามารถอัปเดตได้ จะขอบคุณความช่วยเหลือใด ๆ


4
.map(Number)มีความซ้ำซ้อนเนื่องจาก*โอเปอเรเตอร์จะรวมค่าเป็นตัวเลขต่อไป ;-)
RobG

4
คำถามสองสามข้อ: 1) คุณตั้งใจจะจัดการกับจำนวนลบอย่างไร? ยกตัวอย่างเช่นจำนวน-57เป็นจริง-50และ-7.. เมื่อมองไปที่วิธีการนี้ก็จะทำการลดลงของ-5x ผลผลิตเป็นจำนวนบวก-7 35หรือคุณต้องการให้เห็นเฉพาะเครื่องหมายลบด้วย5และไม่ใช่7, แม้แต่สรรพสิ่ง7ก็เป็นลบเช่นกัน 2) คุณตั้งใจจะจัดการกับตัวเลขซึ่งรวมถึงศูนย์ได้อย่างไร? เช่นนี้จะเป็นศูนย์การลดโดยอัตโนมัติ ดังนั้นยิ่งคุณผ่านจำนวนมากเท่าไหร่โอกาสที่จะเป็นศูนย์ก็จะยิ่งมากขึ้นเท่านั้น ตัวเลือกอื่นจะข้ามศูนย์
Pimp Trizkit

3
ฉันรู้ว่าคำถามข้างต้นของฉันไม่ได้เกี่ยวกับการนับการเรียกซ้ำ แต่เป็นเพียงแค่การไขปริศนาของเนื้อหาที่ใช้ในคำถามนี้ กรุณายกโทษให้ฉัน.
Pimp Trizkit

3
ฉันภูมิใจที่คุณชอบคำตอบของฉัน แต่สำหรับวัตถุประสงค์ในทางปฏิบัติฉันคิดว่าstackoverflow.com/a/59570894/1346276เป็นตัวแปรทั่วไปที่สะอาดที่สุด
phipsgabler

2
@phipsgabler ใครก็ตามที่ใช้เวลาในการเขียนคำตอบที่ชาญฉลาดและสอดคล้องกันสมควรได้รับเช่น ขอบคุณ
chs242

คำตอบ:


25

นี่เป็นตัวแปรทางวิชาการเกือบทั้งหมด แต่คุณสามารถใช้combinator จุดคงที่ที่ปรับเปลี่ยนเพื่อจุดประสงค์นี้

ให้ย่อและปรับปรุงฟังก์ชั่นดั้งเดิมของคุณสักหน่อย:

function singleDigit(n) {
    let digitProduct = [...(n + '')].reduce((x, y) => x * y, 1);
    return digitProduct <= 9 ? digitProduct : singleDigit(digitProduct);
}

// singleDigit(123234234) == 0

จากตัวแปรนี้เราสามารถแยกแยะและเรียกสายเรียกซ้ำ:

function singleDigitF(recur) {
    return function (n) {
        let digitProduct = [...(n + '')].reduce((x, y) => x * y, 1);
        return digitProduct <= 9 ? digitProduct : recur()(digitProduct);
    };
}

ตอนนี้ฟังก์ชั่นนี้สามารถใช้ร่วมกับ combinator จุดคงที่ โดยเฉพาะฉันใช้ Y combinator ดัดแปลงสำหรับ (เข้มงวด) JavaScript ดังนี้

function Ynormal(f, ...args) {
    let Y = (g) => g(() => Y(g));
    return Y(f)(...args);
}

Ynormal(singleDigitF, 123234234) == 0ที่เรามี

ตอนนี้เคล็ดลับมา เนื่องจากเราแยกตัวประกอบการเรียกซ้ำไปยัง combinator Y เราจึงสามารถนับจำนวนการสอบถามซ้ำภายใน:

function Ycount(f, ...args) {
    let count = 1;
    let Y = (g) => g(() => {count += 1; return Y(g);});
    return [Y(f)(...args), count];
}

ตรวจสอบอย่างรวดเร็วใน Node REPL ให้:

> Ycount(singleDigitF, 123234234)
[ 0, 3 ]
> let digitProduct = (n) => [...(n + '')].reduce((x, y) => x * y, 1)
undefined
> digitProduct(123234234)
3456
> digitProduct(3456)
360
> digitProduct(360)
0
> Ycount(singleDigitF, 39)
[ 4, 3 ]

Combinator นี้จะทำงานสำหรับการนับจำนวนการโทรในใด ๆsingleDigitFฟังก์ชันเวียนเขียนในรูปแบบของ

(โปรดทราบว่ามีสองแหล่งที่มาของการได้รับศูนย์เป็นคำตอบที่บ่อยมาก: ล้นตัวเลข ( 123345456999999999กลายเป็น123345457000000000ฯลฯ ) และความจริงที่ว่าคุณจะได้รับเกือบเป็นศูนย์เป็นค่ากลางที่ไหนสักแห่งเมื่อขนาดของการป้อนข้อมูลเติบโตขึ้น)


6
สำหรับผู้ลงคะแนนเสียง: ฉันเห็นด้วยกับคุณจริงๆว่านี่ไม่ใช่วิธีการแก้ปัญหาที่ดีที่สุด - นั่นคือเหตุผลที่ฉันนำหน้ามันเป็น "วิชาการล้วนๆ"
phipsgabler

สุจริตมันเป็นคำตอบที่ยอดเยี่ยมและเหมาะอย่างยิ่งสำหรับประเภทการถดถอย / คณิตศาสตร์ของคำถามเดิม
Sheraff

73

คุณควรเพิ่มอาร์กิวเมนต์ตัวนับให้กับนิยามฟังก์ชันของคุณ:

function singleDigit(num, counter = 0) {
    console.log(`called ${counter} times`)
    //...
    return singleDigit(number, counter+1)
}
singleDigit(39)

6
น่ากลัว ดูเหมือนว่าเคาน์เตอร์ของฉันไม่ทำงานเพราะฉันประกาศในฟังก์ชั่น
chs242

7
@ chs242 กฎของขอบเขตจะกำหนดว่าการประกาศในฟังก์ชันจะสร้างขึ้นมาใหม่แต่ละการร้องขอ stackoverflow.com/questions/500431/…
Taplar

10
@ chs242 ไม่ใช่ว่าคุณประกาศไว้ในฟังก์ชั่น ในทางเทคนิคนั่นคือพารามิเตอร์เริ่มต้นทั้งหมดก็ทำได้เช่นกัน - ในกรณีของคุณเป็นเพียงค่าที่ไม่เคยมีในครั้งต่อไปที่ฟังก์ชันถูกเรียกซ้ำ AE ทุกครั้งที่ฟังก์ชั่นการทำงานcounterจะได้รับทิ้งและชุด0, เว้นแต่คุณอย่างชัดเจนพกมันไปในโทร recursive ของคุณเป็น Sheraff ไม่ AesingleDigit(number, ++counter)
zfrisch

2
ใช่ @zfrisch ฉันเข้าใจแล้วว่าตอนนี้ ขอบคุณที่สละเวลาอธิบาย
chs242

35
กรุณาเปลี่ยนไป++counter counter+1มันทำงานได้เทียบเท่า แต่อย่างหลังระบุถึงเจตนาที่ดีกว่าไม่กลายพันธุ์และพารามิเตอร์ (โดยไม่จำเป็น) และไม่มีความเป็นไปได้ที่จะเกิดการเพิ่มขึ้นภายหลังโดยไม่ตั้งใจ หรือดีกว่าเนื่องจากเป็นระบบโทรหางให้ใช้การวนซ้ำแทน
BlueRaja - Danny Pflughoeft

37

วิธีการแก้ปัญหาแบบดั้งเดิมคือการส่งผ่านการนับเป็นพารามิเตอร์ไปยังฟังก์ชั่นตามที่แนะนำโดยคำตอบอื่น

อย่างไรก็ตามมีวิธีแก้ปัญหาอื่นใน js คำตอบอื่น ๆ ที่แนะนำเพียงการนับจำนวนนอกฟังก์ชั่นวนซ้ำ:

let counter = 0
function singleDigit(num) {
  counter++;
  // ..
}

หลักสูตรนี้ใช้งานได้จริง อย่างไรก็ตามสิ่งนี้ทำให้ฟังก์ชันที่ไม่ใช่ reentrant (ไม่สามารถเรียกได้อย่างถูกต้องสองครั้ง) ในบางกรณีคุณสามารถเพิกเฉยต่อปัญหานี้และให้แน่ใจว่าคุณไม่ได้โทรมาsingleDigitสองครั้ง (จาวาสคริปต์เป็นเธรดเดี่ยวดังนั้นจึงไม่ยากเกินไปที่จะทำ) แต่นี่เป็นข้อผิดพลาดที่รอให้เกิดขึ้นหากคุณอัปเดตในsingleDigitภายหลังเป็นแบบอะซิงโครนัส น่าเกลียด

ทางออกคือการประกาศcounterตัวแปรภายนอก แต่ไม่ใช่ทั่วโลก สิ่งนี้เป็นไปได้เพราะจาวาสคริปต์ปิด:

function singleDigit(num) {
  let counter = 0; // outside but in a closure

  // use an inner function as the real recursive function:
  function recursion (num) {
    counter ++
    let number = [...num + ''].map(Number).reduce((x, y) => {return x * y})

    if(number <= 9){
      return counter            // return final count (terminate)
    }else{
      return recursion(number)  // recurse!
    }
  }

  return recursion(num); // start recursion
}

สิ่งนี้คล้ายกับโซลูชันระดับโลก แต่ทุกครั้งที่คุณเรียกใช้singleDigit(ซึ่งตอนนี้ไม่ใช่ฟังก์ชันแบบเรียกซ้ำ) มันจะสร้างอินสแตนซ์ใหม่ของcounterตัวแปร


1
ตัวแปรตัวนับใช้ได้เฉพาะในsingleDigitฟังก์ชั่นและเป็นทางเลือกใหม่ในการทำเช่นนี้โดยไม่ต้องผ่านการโต้แย้ง +1
AndrewL64

1
เนื่องจากrecursionตอนนี้ถูกแยกออกอย่างสมบูรณ์จึงควรปลอดภัยอย่างยิ่งที่จะส่งตัวนับเป็นพารามิเตอร์สุดท้าย ฉันไม่คิดว่าจำเป็นต้องสร้างฟังก์ชั่นภายใน หากคุณไม่ชอบความคิดที่ว่ามีพารามิเตอร์เพื่อผลประโยชน์ของการเรียกซ้ำ (ฉันใช้จุดที่ผู้ใช้สามารถยุ่งกับสิ่งเหล่านั้น) จากนั้นล็อคพวกเขาด้วยFunction#bindในฟังก์ชั่นที่ใช้บางส่วน
บังคับบัญชาที่กำหนดเอง

@customcommander ใช่ผมกล่าวถึงนี้ในการสรุปในส่วนแรกของคำตอบของฉัน the traditional solution is to pass the count as a parameter- นี่เป็นทางเลือกอื่นในภาษาที่มีการปิด ในบางวิธีมันง่ายกว่าที่จะติดตามเพราะมันเป็นเพียงตัวแปรเดียวแทนที่จะเป็นจำนวนอินสแตนซ์ตัวแปรที่ไม่มีที่สิ้นสุด ในรูปแบบอื่นรู้การแก้ปัญหานี้จะช่วยให้เมื่อสิ่งที่คุณกำลังติดตามเป็นวัตถุที่ใช้ร่วมกัน (จินตนาการสร้างแผนที่ที่ไม่ซ้ำกัน) หรือวัตถุที่มีขนาดใหญ่มาก (เช่นสตริง HTML)
slebetman

counter--จะเป็นวิธีดั้งเดิมในการแก้ปัญหาการเรียกร้องของคุณ "ไม่สามารถเรียกได้อย่างถูกต้องสองครั้ง"
MonkeyZeus

1
@MonkeyZeus มันมีความแตกต่างอะไรบ้าง? นอกจากนี้คุณจะทราบได้อย่างไรว่าจำนวนใดที่เริ่มต้นตัวนับเพื่อดูว่าเป็นจำนวนที่เราต้องการค้นหาหรือไม่
Slebetman

22

อีกวิธีหนึ่งเนื่องจากคุณสร้างตัวเลขทั้งหมดคือใช้ตัวสร้าง

องค์ประกอบสุดท้ายคือจำนวนของคุณnลดลงเป็นตัวเลขหลักเดียวและเพื่อนับจำนวนครั้งที่คุณทำซ้ำเพียงแค่อ่านความยาวของอาร์เรย์

const digits = [...to_single_digit(39)];
console.log(digits);
//=> [27, 14, 4]
<script>
function* to_single_digit(n) {
  do {
    n = [...String(n)].reduce((x, y) => x * y);
    yield n;
  } while (n > 9);
}
</script>


ความคิดสุดท้าย

คุณอาจต้องการพิจารณาว่ามีเงื่อนไขการคืนสินค้าก่อนหน้าในการทำงานของคุณ ตัวเลขใด ๆ ที่มีค่าศูนย์จะส่งกลับค่าศูนย์

singleDigit(1024);       //=> 0
singleDigit(9876543210); //=> 0

// possible solution: String(n).includes('0')

เช่นเดียวกันสามารถพูดได้สำหรับตัวเลขใด ๆ ที่ทำจาก1เท่านั้น

singleDigit(11);    //=> 1
singleDigit(111);   //=> 1
singleDigit(11111); //=> 1

// possible solution: [...String(n)].every(n => n === '1')

สุดท้ายคุณไม่ได้ชี้แจงว่าคุณยอมรับจำนวนเต็มบวกเท่านั้นหรือไม่ หากคุณยอมรับจำนวนเต็มลบการส่งสตริงไปยังสตริงอาจมีความเสี่ยง:

[...String(39)].reduce((x, y) => x * y)
//=> 27

[...String(-39)].reduce((x, y) => x * y)
//=> NaN

ทางออกที่เป็นไปได้:

const mult = n =>
  [...String(Math.abs(n))].reduce((x, y) => x * y, n < 0 ? -1 : 1)

mult(39)
//=> 27

mult(-39)
//=> -27

ยิ่งใหญ่ @customcommander ขอบคุณที่อธิบายอย่างชัดเจน
chs242

6

มีคำตอบที่น่าสนใจมากมายที่นี่ ฉันคิดว่ารุ่นของฉันมีทางเลือกที่น่าสนใจเพิ่มเติม

คุณทำหลายสิ่งด้วยฟังก์ชันที่คุณต้องการ คุณลดมันซ้ำ ๆ เป็นหลักเดียว คุณบันทึกค่ากลางและคุณต้องการนับการโทรซ้ำ วิธีหนึ่งในการจัดการทั้งหมดนี้คือการเขียนฟังก์ชั่นบริสุทธิ์ซึ่งจะส่งกลับโครงสร้างข้อมูลที่มีผลสุดท้ายขั้นตอนที่ดำเนินการและการโทรนับทั้งหมดในที่เดียว:

  {
    digit: 4,
    steps: [39, 27, 14, 4],
    calls: 3
  }

จากนั้นคุณสามารถบันทึกขั้นตอนหากคุณต้องการหรือเก็บไว้เพื่อการประมวลผลเพิ่มเติม

นี่คือรุ่นที่ทำเช่นนั้น:

const singleDigit = (n, steps = []) =>
  n <= 9
    ? {digit: n, steps: [... steps, n], calls: steps .length}
    : singleDigit ([... (n + '')] .reduce ((a, b) => a * b), [... steps, n])

console .log (singleDigit (39))

โปรดทราบว่าเราติดตามแต่ได้รับมาsteps callsในขณะที่เราสามารถติดตามจำนวนการโทรด้วยพารามิเตอร์เพิ่มเติมที่ดูเหมือนจะไม่ได้อะไรเลย นอกจากนี้เรายังข้ามmap(Number)ขั้นตอน - การคูณเหล่านี้จะถูกรวมเข้ากับตัวเลขในกรณีใด ๆ

หากคุณมีข้อกังวลเกี่ยวกับstepsพารามิเตอร์ที่เป็นค่าเริ่มต้นซึ่งจะถูกเปิดเผยเป็นส่วนหนึ่งของ API ของคุณมันง่ายพอที่จะซ่อนไว้โดยใช้ฟังก์ชันภายในเช่นนี้:

const singleDigit = (n) => {
  const recur = (n, steps) => 
    n <= 9
      ? {digit: n, steps: [... steps, n], calls: steps .length}
      : recur ([... (n + '')] .reduce ((a, b) => a * b), [... steps, n])
  return recur (n, [])
}

และในทั้งสองกรณีมันอาจจะสะอาดกว่าถ้าแยกการคูณหลักลงในฟังก์ชันผู้ช่วย:

const digitProduct = (n) => [... (n + '')] .reduce ((a, b) => a * b)

const singleDigit = (n, steps = []) =>
  n <= 9
    ? {digit: n, steps: [... steps, n], calls: steps .length}
    : singleDigit (digitProduct(n), [... steps, n])

2
คำตอบที่ยอดเยี่ยมอีกข้อหนึ่ง) โปรดทราบว่าเมื่อnเป็นลบdigitProductจะได้ผลลัพธ์NaN( -39 ~> ('-' * '3') * '9') ดังนั้นคุณอาจต้องการใช้ค่าสัมบูรณ์ของnและใช้-1หรือ1เป็นค่าเริ่มต้นของการลดของคุณ
บังคับบัญชาประจำ

@customcommander: จริงก็จะกลับตั้งแต่{"digit":-39,"steps":[-39],"calls":0} -39 < 9ในขณะที่ฉันยอมรับว่าสิ่งนี้อาจทำอย่างไรกับการตรวจสอบข้อผิดพลาดบางอย่าง: พารามิเตอร์เป็นตัวเลขหรือไม่ - เป็นจำนวนเต็มบวกหรือไม่ - ฯลฯ ฉันไม่คิดว่าฉันจะอัปเดตเพื่อรวมสิ่งนั้น สิ่งนี้จะจับอัลกอริทึมและการจัดการข้อผิดพลาดมักจะเป็นรหัสเฉพาะของฐาน
Scott Sauyet

6

หากคุณเพียงแค่พยายามนับจำนวนครั้งที่มันลดลงและไม่สนใจการสอบถามซ้ำโดยเฉพาะ ... คุณสามารถลบการสอบถามซ้ำได้ รหัสด้านล่างยังคงซื่อสัตย์ต่อโพสต์ต้นฉบับเนื่องจากไม่นับnum <= 9ว่าจำเป็นต้องลด ดังนั้นsingleDigit(8)จะมีcount = 0และsingleDigit(39)จะมีcount = 3เช่นเดียวกับ OP และคำตอบที่ยอมรับได้แสดงให้เห็น:

const singleDigit = (num) => {
    let count = 0, ret, x;
    while (num > 9) {
        ret = 1;
        while (num > 9) {
            x = num % 10;
            num = (num - x) / 10;
            ret *= x;
        }
        num *= ret;
        count++;
        console.log(num);
    }
    console.log("Answer = " + num + ", count = " + count);
    return num;
}

ไม่จำเป็นสำหรับการประมวลผลหมายเลข 9 หรือน้อยกว่า (เช่นnum <= 9) น่าเสียดายที่รหัส OP จะดำเนินการnum <= 9แม้จะไม่ได้นับก็ตาม รหัสข้างต้นจะไม่ประมวลผลหรือนับnum <= 9เลย มันเพิ่งผ่านมันไป

ฉันเลือกที่จะไม่ใช้.reduceเพราะการทำคณิตศาสตร์จริงนั้นทำได้เร็วกว่ามาก และสำหรับฉันเข้าใจง่ายขึ้น


คิดเพิ่มเติมเกี่ยวกับความเร็ว

ฉันรู้สึกว่ารหัสที่ดียังรวดเร็ว หากคุณใช้การลดแบบนี้ (ซึ่งใช้ในการคำนวณจำนวนมาก) คุณอาจต้องใช้มันกับข้อมูลจำนวนมาก ในกรณีนี้ความเร็วจะกลายเป็นความสำคัญสูงสุด

การใช้ทั้งสอง.map(Number)และconsole.log(ในแต่ละขั้นตอนการลดขนาด) มีความยาวมากในการดำเนินการและไม่จำเป็น เพียงแค่ลบ.map(Number)ออกจาก OP เร่งมันขึ้นประมาณ 4.38x ลบconsole.logsped มันมากจนแทบจะเป็นไปไม่ได้ที่จะทดสอบอย่างถูกต้อง (ฉันไม่ต้องการรอ)

ดังนั้นคล้ายกับcustomcommander 's คำตอบไม่ได้ใช้.map(Number)มิได้console.logและผลักดันผลเป็นอาร์เรย์และใช้.lengthสำหรับcountเป็นมากได้เร็วขึ้นมาก แต่น่าเสียดายสำหรับcustomcommander 's คำตอบโดยใช้ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าเป็นจริงๆช้า (ว่าคำตอบเป็นเรื่องเกี่ยวกับ 2.68x ช้ากว่า OP โดยไม่ต้อง.map(Number)และconsole.log)

นอกจากนี้แทนที่จะใช้.reduceฉันเพิ่งใช้คณิตศาสตร์จริง การเปลี่ยนแปลงครั้งเดียวเพียงอย่างเดียวนี้ทำให้รุ่นของฉันใช้งานได้เร็วขึ้นด้วยปัจจัย 3.59 เท่า

ในที่สุดการเรียกซ้ำช้ากว่าจะใช้พื้นที่สแต็กใช้หน่วยความจำมากขึ้นและมีการ จำกัด จำนวนครั้งที่ "recur" สามารถซ้ำได้ หรือในกรณีนี้สามารถลดขั้นตอนได้กี่ขั้นตอนเพื่อให้การลดลงเสร็จสมบูรณ์ การเปิดตัวการวนซ้ำของคุณไปยังลูปวนซ้ำทำให้มันทั้งหมดอยู่ในที่เดียวกันบนสแต็กและไม่มีข้อ จำกัด ทางทฤษฎีเกี่ยวกับจำนวนขั้นตอนการลดที่สามารถใช้ให้เสร็จ ดังนั้นฟังก์ชั่นเหล่านี้สามารถ "ลด" จำนวนเต็มเกือบทุกขนาด จำกัด ด้วยเวลาดำเนินการและระยะเวลาที่อาร์เรย์สามารถ

ทั้งหมดนี้อยู่ในใจ ...

const singleDigit2 = (num) => {
    let red, x, arr = [];
    do {
        red = 1;
        while (num > 9) {
            x = num % 10;
            num = (num - x) / 10;
            red *= x;
        }
        num *= red;
        arr.push(num);
    } while (num > 9);
    return arr;
}

let ans = singleDigit2(39);
console.log("singleDigit2(39) = [" + ans + "],  count = " + ans.length );
 // Output: singleDigit2(39) = [27,14,4],  count = 3

ฟังก์ชั่นด้านบนทำงานเร็วมาก มันเร็วกว่า OP ประมาณ 3.13x .map(Number)และไม่console.logเร็วกว่า8.4 เท่าเมื่อเทียบกับคำตอบของCustomcommander โปรดทราบว่าการลบconsole.logออกจาก OP จะป้องกันไม่ให้สร้างหมายเลขในแต่ละขั้นตอนของการลด ดังนั้นความต้องการที่นี่เพื่อผลักดันผลลัพธ์เหล่านี้ลงในอาร์เรย์

PT


1
คำตอบนี้มีคุณค่าด้านการศึกษามากมายขอบคุณมากสำหรับสิ่งนั้น I feel good code is also fast.ผมอยากบอกว่าคุณภาพรหัสได้ที่จะวัดกับชุดที่กำหนดไว้ล่วงหน้าของความต้องการ หากประสิทธิภาพไม่ใช่หนึ่งในนั้นคุณจะไม่ได้รับอะไรเลยโดยแทนที่โค้ดที่ทุกคนสามารถเข้าใจได้ด้วยโค้ด "เร็ว" คุณจะไม่เชื่อว่าจำนวนของรหัสที่ฉันได้เห็นนั้นได้รับการปรับสภาพให้มีประสิทธิภาพจนถึงจุดที่ไม่มีใครสามารถเข้าใจได้อีกต่อไป (ด้วยเหตุผลบางอย่างรหัสที่ดีที่สุดมักจะไม่มีเอกสาร) ในที่สุดก็ทราบว่ารายการที่สร้างขี้เกียจอนุญาตให้หนึ่งรายการที่จะบริโภครายการตามความต้องการ
บังคับบัญชาประจำ

ขอบคุณฉันคิดว่า IMHO การอ่านคณิตศาสตร์ที่แท้จริงของการทำมันง่ายกว่าที่จะเข้าใจสำหรับฉัน .. มากกว่าคำแถลง[...num+''].map(Number).reduce((x,y)=> {return x*y})หรือแม้กระทั่ง[...String(num)].reduce((x,y)=>x*y)ที่ฉันเห็นในคำตอบส่วนใหญ่ที่นี่ ดังนั้นสำหรับฉันนี่เป็นประโยชน์เพิ่มเติมของความเข้าใจที่ดีขึ้นเกี่ยวกับสิ่งที่เกิดขึ้นในแต่ละรอบและเร็วกว่ามาก ใช่โค้ดย่อ (ซึ่งมีที่อยู่) นั้นอ่านได้ยากมาก แต่ในกรณีเหล่านี้เรามักจะไม่สนใจเรื่องการอ่าน แต่เป็นเพียงผลลัพธ์สุดท้ายที่จะตัดและวางและเดินหน้าต่อไป
Pimp Trizkit

JavaScript ไม่มีการแบ่งจำนวนเต็มเพื่อให้คุณสามารถเทียบเท่า C ได้digit = num%10; num /= 10;หรือไม่ ต้องทำnum - xก่อนที่จะลบหลักท้ายก่อนที่จะแบ่งมีแนวโน้มที่จะบังคับให้คอมไพเลอร์ JIT ที่จะทำส่วนที่แยกต่างหากจากที่มันได้รับส่วนที่เหลือ
Peter Cordes

ฉันไม่คิดอย่างนั้นนี่คือvars (JS ไม่มีints) ดังนั้นn /= 10;จะแปลงnเป็นลอยถ้าจำเป็น num = num/10 - x/10อาจแปลงเป็นทุ่นซึ่งเป็นรูปแบบยาวของสมการ ดังนั้นฉันต้องใช้เวอร์ชันที่ปรับโครงสร้างใหม่num = (num-x)/10;เพื่อให้เป็นจำนวนเต็มไม่มีวิธีที่ฉันสามารถหาได้ใน JavaScript ที่สามารถให้ทั้งความฉลาดและส่วนที่เหลือของการดำเนินการหารเดียว นอกจากนี้ยังdigit = num%10; num /= 10;มีข้อความสองข้อความที่แยกกันและทำให้การดำเนินงานของแผนกสองแยกเป็นเวลานานมาแล้วที่ฉันใช้ C แต่ฉันคิดว่ามันเป็นความจริงเช่นกัน
Pimp Trizkit

6

ทำไมไม่โทรไปที่console.countฟังก์ชั่นของคุณ?

แก้ไข: ตัวอย่างข้อมูลเพื่อลองในเบราว์เซอร์ของคุณ:

function singleDigit(num) {
    console.count("singleDigit");

    let counter = 0
    let number = [...num + ''].map(Number).reduce((x, y) => {return x * y})

    if(number <= 9){
        console.log(number)
    }else{
        console.log(number)
        return singleDigit(number), counter += 1
    }
}
singleDigit(39)

ฉันใช้งานได้ใน Chrome 79 และ Firefox 72


console.count จะไม่ช่วยให้ตัวนับได้รับการรีเซ็ตทุกครั้งที่มีการเรียกใช้ฟังก์ชั่น (ตามที่ได้รับการอธิบายในคำตอบข้างต้น)
chs242

2
ฉันไม่เข้าใจปัญหาของคุณเนื่องจากฉันใช้งานได้ใน Chrome และ Firefox ฉันได้เพิ่มส่วนย่อยในคำตอบของฉัน
Mistermatt

6

คุณสามารถใช้การปิดสำหรับสิ่งนี้

เพียงแค่เก็บcounterไว้ในฟังก์ชั่นปิด

นี่คือตัวอย่าง:

function singleDigitDecorator() {
	let counter = 0;

	return function singleDigitWork(num, isCalledRecursively) {

		// Reset if called with new params 
		if (!isCalledRecursively) {
			counter = 0;
		}

		counter++; // *

		console.log(`called ${counter} times`);

		let number = [...(num + "")].map(Number).reduce((x, y) => {
			return x * y;
		});

		if (number <= 9) {
			console.log(number);
		} else {
			console.log(number);

			return singleDigitWork(number, true);
		}
	};
}

const singleDigit = singleDigitDecorator();

singleDigit(39);

console.log('`===========`');

singleDigit(44);


1
แต่วิธีนี้ตัวนับยังคงนับในการโทรครั้งต่อไปจะต้องรีเซ็ตในการโทรครั้งแรกแต่ละครั้ง มันนำไปสู่คำถามที่ยาก: จะบอกได้อย่างไรว่ามีการเรียกใช้ฟังก์ชันเรียกซ้ำจากบริบทที่แตกต่างกันในกรณีนี้คือฟังก์ชั่น global vs
RobG

นี่เป็นเพียงตัวอย่างเพื่อคิดขึ้นมา อาจมีการแก้ไขโดยขอให้ผู้ใช้สำหรับความต้องการของเขา
Kholiavko

@RobG ฉันไม่เข้าใจคำถามของคุณ ไม่สามารถเรียกใช้ฟังก์ชันเรียกซ้ำนอกการปิดได้เนื่องจากเป็นฟังก์ชันภายใน ดังนั้นจึงไม่มีความเป็นไปได้หรือต้องการแยกแยะบริบทเนื่องจากมีบริบทที่เป็นไปได้เพียงข้อเดียวเท่านั้น
slebetman

@slebetman ตัวนับจะไม่ถูกรีเซ็ต ฟังก์ชั่นที่ส่งคืนโดยsingleDigitDecorator()จะเพิ่มตัวนับเดียวกันทุกครั้งที่เรียกใช้
บังคับบัญชาที่กำหนดเอง

1
@ slebetman ปัญหาคือฟังก์ชั่นที่ส่งคืนโดยsingleDigitDecoratorไม่รีเซ็ตตัวนับเมื่อถูกเรียกอีกครั้ง นั่นคือฟังก์ชั่นที่จำเป็นต้องรู้เมื่อต้องรีเซ็ตตัวนับมิฉะนั้นจะต้องใช้อินสแตนซ์ใหม่ของฟังก์ชันสำหรับการใช้งานแต่ละครั้ง กรณีการใช้งานที่เป็นไปได้สำหรับFunction.caller ? ;-)
RobG

1

นี่เป็นเวอร์ชั่น Python ที่ใช้ฟังก์ชั่น wrapper เพื่อทำให้ตัวนับง่ายขึ้นตามที่ได้รับคำแนะนำจากคำตอบของ slebetman - ฉันเขียนสิ่งนี้เพียงเพราะความคิดหลักมีความชัดเจนในการใช้งานนี้:

from functools import reduce

def single_digit(n: int) -> tuple:
    """Take an integer >= 0 and return a tuple of the single-digit product reduction
    and the number of reductions performed."""

    def _single_digit(n, i):
        if n <= 9:
            return n, i
        else:
            digits = (int(d) for d in str(n))
            product = reduce(lambda x, y: x * y, digits)
            return _single_digit(product, i + 1)

    return _single_digit(n, 0)

>>> single_digit(39)
(4, 3)

1
ในหลาม, ฉันไม่ต้องการอะไรเช่นนี้
phipsgabler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.