Melacak berapa kali fungsi rekursif dipanggil

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)

Kode di atas mengambil integer dan menguranginya menjadi satu digit dengan mengalikannya dengan digitnya sendiri.

Contohnya adalah 39.

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

Konsol akan masuk:

27 
14 
4

Bagaimana saya melacak bahwa fungsi rekursif dipanggil 3 kali?

Saya telah mencoba menambahkan penghitung tetapi gagal memperbarui. Akan menghargai bantuan apa pun

chs242
sumber
4
.map(Number)redundan karena *operator memaksa nilai ke angka. ;-)
RobG
4
Beberapa pertanyaan: 1) Bagaimana Anda berniat berurusan dengan angka negatif? Misalnya, bilangan -57benar-benar a -50dan -7.. ketika dilihat dengan cara ini, ia akan melakukan pengurangan -5x -7menghasilkan bilangan positif 35. Atau apakah Anda ingin hanya melihat tanda negatif dengan 5dan bukan 7, bahkan yang 7sebenarnya negatif juga. 2) Bagaimana Anda berniat berurusan dengan angka yang termasuk nol? karena ini akan secara otomatis nol pengurangan. Oleh karena itu, semakin besar angka yang Anda berikan, semakin besar kemungkinannya nol. Pilihan lain adalah dengan melewatkan nol
Pimp Trizkit
3
Saya menyadari pertanyaan saya di atas bukan tentang menghitung rekursi, tetapi lebih pada aspek pemecahan teka-teki dari konten yang digunakan dalam pertanyaan ini. Tolong maafkan saya.
Pimp Trizkit
3
Saya tersanjung bahwa Anda menyukai jawaban saya, tetapi untuk tujuan praktis, saya pikir stackoverflow.com/a/59570894/1346276 adalah varian umum paling bersih.
phipsgabler
2
@phipsgabler siapa pun yang meluangkan waktu untuk menulis jawaban yang cerdas dan koheren layak mendapat suka. Terima kasih
chs242

Jawaban:

25

Ini adalah varian yang hampir murni akademis, tetapi Anda dapat menggunakan kombinator titik tetap yang dimodifikasi untuk tujuan ini.

Mari kita persingkat dan tingkatkan fungsi asli Anda sedikit:

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

// singleDigit(123234234) == 0

Dari varian ini, kita dapat memfaktorkan dan mengeringkan panggilan rekursif:

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

Fungsi ini sekarang dapat digunakan dengan kombinator titik tetap; khusus saya menerapkan kombinator Y yang diadaptasi untuk JavaScript (ketat) sebagai berikut:

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

dimana kita miliki Ynormal(singleDigitF, 123234234) == 0.

Sekarang tibalah kiatnya. Karena kami telah memperhitungkan rekursi ke kombinator Y, kami dapat menghitung jumlah rekursi di dalamnya:

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

Pemeriksaan cepat di Node REPL memberi:

> 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 ini sekarang akan berfungsi untuk menghitung jumlah panggilan dalam fungsi rekursif apa pun yang ditulis dengan gaya singleDigitF.

(Perhatikan bahwa ada dua sumber untuk mendapatkan nol sebagai jawaban yang sangat sering: pelimpahan numerik ( 123345456999999999menjadi 123345457000000000dll.), Dan fakta bahwa Anda hampir pasti akan mendapatkan nol sebagai nilai perantara di suatu tempat, ketika ukuran input bertambah.)

phipsgabler
sumber
6
Kepada para downvoters: Saya benar-benar setuju dengan Anda bahwa ini bukan solusi praktis terbaik - itulah sebabnya saya awali sebagai "murni akademis".
phipsgabler
Jujur, ini solusi yang luar biasa dan sangat cocok untuk jenis regresi / matematika dari pertanyaan awal.
Sheraff
73

Anda harus menambahkan argumen balasan ke definisi fungsi Anda:

function singleDigit(num, counter = 0) {
    console.log(`called ${counter} times`)
    //...
    return singleDigit(number, counter+1)
}
singleDigit(39)
Sheraff
sumber
6
luar biasa. Sepertinya penghitung saya tidak berfungsi karena saya mendeklarasikannya dalam fungsi
chs242
7
@ chs242 aturan ruang lingkup akan menentukan bahwa mendeklarasikannya dalam fungsi akan membuat yang baru setiap doa. stackoverflow.com/questions/500431/…
Taplar
10
@ chs242 bukan berarti Anda mendeklarasikannya dalam fungsi. Secara teknis hanya itu yang dilakukan parameter standar - dalam kasus Anda, hanya saja nilai tersebut tidak pernah terbawa ke waktu berikutnya ketika fungsi dipanggil secara rekursif. setiap kali fungsi berjalan counterakan dihapus dan diatur ke 0, kecuali jika Anda secara eksplisit meneruskannya dalam panggilan rekursif Anda seperti yang dilakukan Sheraff. AesingleDigit(number, ++counter)
zfrisch
2
right @ zfrisch Saya mengerti itu sekarang. Terima kasih telah meluangkan waktu untuk menjelaskannya
chs242
35
Silakan ubah ++counterke counter+1. Mereka setara secara fungsional, tetapi yang terakhir menentukan niat lebih baik, tidak (tidak perlu) bermutasi dan parameter, dan tidak memiliki kemungkinan secara tidak sengaja pasca-kenaikan. Atau lebih baik lagi, karena ini adalah buntut, gunakan loop sebagai gantinya.
BlueRaja - Danny Pflughoeft
37

Solusi tradisional adalah melewatkan hitungan sebagai parameter ke fungsi seperti yang disarankan oleh jawaban lain.

Namun, ada solusi lain di js. Beberapa jawaban lain yang disarankan hanya menyatakan penghitungan di luar fungsi rekursif:

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

Ini tentu saja berhasil. Namun ini membuat fungsi ini tidak reentrant (tidak dapat dipanggil dua kali dengan benar). Dalam beberapa kasus, Anda dapat mengabaikan masalah ini dan cukup memastikan Anda tidak menelepon singleDigitdua kali (javascript adalah utas tunggal sehingga tidak terlalu sulit untuk dilakukan) tetapi ini adalah bug yang menunggu untuk terjadi jika Anda memperbarui singleDigitkemudian menjadi asinkron dan juga terasa jelek.

Solusinya adalah mendeklarasikan countervariabel di luar tetapi tidak secara global. Ini dimungkinkan karena javascript memiliki penutupan:

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
}

Ini mirip dengan solusi global tetapi setiap kali Anda menelepon singleDigit(yang sekarang bukan fungsi rekursif) itu akan membuat contoh baru dari countervariabel.

Slebetman
sumber
1
Variabel penghitung hanya tersedia dalam singleDigitfungsi dan menyediakan cara bersih alternatif untuk melakukan ini tanpa melewati argumen imo. +1
AndrewL64
1
Karena recursionsekarang sepenuhnya terisolasi itu harus benar-benar aman untuk melewati penghitung sebagai parameter terakhir. Saya tidak berpikir membuat fungsi batin itu perlu. Jika Anda tidak menyukai gagasan memiliki parameter untuk satu-satunya manfaat rekursi (saya memang mengambil titik bahwa pengguna dapat mengacaukannya) maka kunci mereka dengan Function#bindfungsi yang diterapkan sebagian.
customcommander
@customcommander Ya, saya menyebutkan ini dalam ringkasan di bagian pertama dari jawaban saya - the traditional solution is to pass the count as a parameter. Ini adalah solusi alternatif dalam bahasa yang memiliki penutup. Dalam beberapa hal lebih mudah untuk diikuti karena hanya satu variabel daripada jumlah instance variabel yang mungkin tak terbatas. Dengan cara lain mengetahui solusi ini membantu ketika hal yang Anda lacak adalah objek bersama (bayangkan membangun peta unik) atau objek yang sangat besar (seperti string HTML)
slebetman
counter--akan menjadi cara tradisional untuk memecahkan klaim Anda "tidak dapat dipanggil dua kali dengan benar"
MonkeyZeus
1
@MonkeyZeus Apa bedanya? Juga, bagaimana Anda tahu nomor apa yang menginisialisasi penghitung untuk melihat bahwa itu adalah jumlah yang ingin kami temukan?
Slebetman
22

Pendekatan lain, karena Anda menghasilkan semua angka, adalah menggunakan generator.

Elemen terakhir adalah jumlah Anda ndikurangi menjadi satu digit angka dan untuk menghitung berapa kali Anda mengulanginya, cukup baca panjang array.

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>


Pikiran terakhir

Anda mungkin ingin mempertimbangkan untuk mengembalikan kondisi awal dalam fungsi Anda. Setiap angka dengan nol di dalamnya akan mengembalikan nol.

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

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

Hal yang sama dapat dikatakan untuk angka apa pun yang dibuat 1hanya.

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

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

Akhirnya, Anda tidak mengklarifikasi apakah Anda hanya menerima bilangan bulat positif. Jika Anda menerima bilangan bulat negatif, maka melemparkannya ke string dapat berisiko:

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

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

Solusi yang mungkin:

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

mult(39)
//=> 27

mult(-39)
//=> -27
customcommander
sumber
Bagus. @customcommander, terima kasih telah menjelaskan ini dengan sangat jelas
chs242
6

Ada banyak jawaban menarik di sini. Saya pikir versi saya menawarkan alternatif menarik tambahan.

Anda melakukan beberapa hal dengan fungsi yang diminta. Anda menguranginya menjadi satu digit secara rekursif. Anda mencatat nilai perantara, dan Anda ingin menghitung panggilan rekursif yang dilakukan. Salah satu cara untuk menangani semua ini adalah dengan menulis fungsi murni yang akan mengembalikan struktur data yang berisi hasil akhir, langkah-langkah yang diambil dan panggilan dihitung semuanya menjadi satu:

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

Anda kemudian dapat mencatat langkah-langkah jika diinginkan, atau menyimpannya untuk diproses lebih lanjut.

Berikut adalah versi yang melakukan itu:

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))

Perhatikan bahwa kami melacak stepstetapi menurunkan calls. Meskipun kami dapat melacak jumlah panggilan dengan parameter tambahan, hal itu sepertinya tidak menghasilkan apa-apa. Kami juga melewatkan map(Number)langkah - ini akan dipaksa ke angka dalam hal apa pun oleh penggandaan.

Jika Anda khawatir tentang stepsparameter default yang diekspos sebagai bagian dari API Anda, cukup mudah untuk menyembunyikannya dengan menggunakan fungsi internal seperti ini:

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, [])
}

Dan dalam kedua kasus itu, mungkin sedikit lebih bersih untuk mengekstrak perkalian digit menjadi fungsi pembantu:

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])
Scott Sauyet
sumber
2
Jawaban hebat lainnya;) Harap dicatat bahwa ketika n negatif, digitProductakan kembali NaN( -39 ~> ('-' * '3') * '9'). Jadi, Anda mungkin ingin menggunakan nilai absolut dari n dan menggunakan -1atau 1sebagai nilai awal dari pengurangan Anda.
customcommander
@customcommander: sebenarnya, itu akan kembali {"digit":-39,"steps":[-39],"calls":0}, karena -39 < 9. Meskipun saya setuju bahwa ini mungkin dilakukan dengan beberapa pengecekan kesalahan: apakah parameternya angka? - apakah bilangan bulat positif? - dll. Saya pikir saya tidak akan memperbarui untuk memasukkan itu. Ini menangkap algoritme, dan penanganan kesalahan sering khusus untuk basis kode seseorang.
Scott Sauyet
6

Jika Anda hanya mencoba menghitung berapa kali berkurang dan tidak peduli tentang rekursi secara khusus ... Anda bisa menghapus rekursi itu. Kode di bawah ini tetap setia ke Posting Asli karena tidak dianggap num <= 9perlu dikurangi. Oleh karena itu, singleDigit(8)akan memiliki count = 0, dan singleDigit(39)akan memiliki count = 3, seperti OP dan jawaban yang diterima menunjukkan:

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;
}

Tidak perlu memproses angka 9 atau kurang (mis. num <= 9). Sayangnya kode OP akan diproses num <= 9walaupun tidak dihitung. Kode di atas tidak akan memproses atau menghitungnum <= 9 sama sekali. Itu hanya melewati itu.

Saya memilih untuk tidak menggunakan .reducekarena melakukan matematika sebenarnya jauh lebih cepat untuk dieksekusi. Dan, bagi saya, lebih mudah dipahami.


Pemikiran lebih lanjut tentang kecepatan

Saya merasa kode yang baik juga cepat. Jika Anda menggunakan jenis pengurangan ini (yang banyak digunakan dalam numerologi), Anda mungkin perlu menggunakannya pada sejumlah besar data. Dalam hal ini, kecepatan akan menjadi yang terpenting.

Menggunakan keduanya .map(Number)dan console.log(pada setiap langkah reduksi) keduanya sangat panjang untuk dieksekusi dan tidak perlu. Cukup menghapus .map(Number)dari OP, mempercepatnya sekitar 4,38x. Menghapus begitu console.logcepat sehingga hampir tidak mungkin untuk menguji dengan benar (saya tidak ingin menunggu untuk itu).

Jadi, mirip dengan jawaban customcommander , tidak menggunakan .map(Number)atau console.logmendorong hasil ke dalam array dan menggunakan .lengthuntuk countjauh lebih cepat. Sayangnya untuk jawaban customcommander , menggunakan fungsi generator benar-benar lambat (jawaban itu sekitar 2.68x lebih lambat daripada OP tanpa .map(Number)dan console.log)

Juga, alih-alih menggunakan .reducesaya hanya menggunakan matematika yang sebenarnya. Perubahan tunggal ini saja mempercepat versi fungsi saya dengan faktor 3,59x.

Akhirnya, rekursi lebih lambat, membutuhkan ruang stack, menggunakan lebih banyak memori, dan memiliki batas berapa kali dapat "berulang". Atau, dalam hal ini, berapa banyak langkah reduksi yang dapat digunakan untuk menyelesaikan reduksi penuh. Meluncurkan rekursi Anda ke loop berulang membuat semuanya berada di tempat yang sama di tumpukan dan tidak memiliki batasan teoritis tentang berapa banyak langkah reduksi yang dapat digunakan untuk menyelesaikannya. Dengan demikian, fungsi-fungsi ini di sini dapat "mengurangi" hampir semua bilangan bulat ukuran, hanya dibatasi oleh waktu eksekusi dan berapa lama array dapat.

Semua ini dalam pikiran ...

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

Fungsi di atas berjalan sangat cepat. Ini sekitar 3.13x lebih cepat dari OP (tanpa .map(Number)dan console.log) dan sekitar 8.4x lebih cepat dari jawaban customcommander . Ingatlah bahwa menghapus console.logOP mencegahnya menghasilkan angka pada setiap langkah pengurangan. Oleh karena itu, kebutuhan di sini untuk mendorong hasil ini ke dalam array.

PT

Pimp Trizkit
sumber
1
Ada banyak nilai pendidikan dalam jawaban ini jadi terima kasih untuk itu. I feel good code is also fast.Saya akan mengatakan bahwa kualitas kode telah diukur terhadap standar set persyaratan. Jika kinerja bukan salah satunya maka Anda tidak mendapatkan apa-apa dengan mengganti kode yang dapat dipahami siapa pun dengan kode "cepat". Anda tidak akan percaya jumlah kode yang saya lihat yang telah dire-refactored menjadi performant sampai tidak ada yang bisa memahaminya lagi (untuk beberapa alasan kode optimal cenderung juga tidak berdokumen;). Akhirnya ketahuilah bahwa daftar yang dibuat dengan malas memungkinkan seseorang untuk mengkonsumsi barang sesuai permintaan.
customcommander
Terima kasih, saya kira. IMHO, membaca matematika yang sebenarnya tentang bagaimana melakukannya lebih mudah dimengerti untuk saya .. daripada pernyataan [...num+''].map(Number).reduce((x,y)=> {return x*y})atau bahkan [...String(num)].reduce((x,y)=>x*y)yang saya lihat di sebagian besar jawaban di sini. Jadi, bagi saya, ini memiliki manfaat tambahan dari pemahaman yang lebih baik tentang apa yang terjadi pada setiap iterasi dan lebih cepat. Ya, kode yang diperkecil (yang memiliki tempatnya) sangat sulit dibaca. Tetapi dalam kasus-kasus itu, seseorang umumnya secara sadar tidak memedulikan keterbacaannya melainkan hasil akhir untuk memotong dan menempel dan melanjutkan.
Pimp Trizkit
Bukankah JavaScript memiliki divisi integer sehingga Anda dapat melakukan hal yang sama dengan C digit = num%10; num /= 10;? Harus melakukan num - xlebih dulu untuk menghapus digit trailing sebelum membaginya kemungkinan akan memaksa kompiler JIT untuk melakukan pembagian terpisah dari yang ia lakukan untuk mendapatkan sisanya.
Peter Cordes
Saya tidak berpikir begitu. Ini adalah var(JS tidak memiliki int). Karenanya, n /= 10;akan dikonversi nmenjadi float jika diperlukan. num = num/10 - x/10mungkin mengubahnya menjadi pelampung, yang merupakan bentuk panjang dari persamaan. Oleh karena itu, saya harus menggunakan versi refactored num = (num-x)/10;untuk menjaganya tetap bilangan bulat. Tidak ada cara yang bisa saya temukan dalam JavaScript yang dapat memberi Anda hasil bagi dan sisa operasi divisi tunggal. Juga, digit = num%10; num /= 10;dua pernyataan terpisah dan dengan demikian dua operasi divisi terpisah. Sudah lama sejak saya menggunakan C, tapi saya pikir itu benar juga.
Pimp Trizkit
6

Mengapa tidak melakukan panggilan ke console.countdalam fungsi Anda?

Edit: Cuplikan untuk dicoba di browser Anda:

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)

Saya memilikinya bekerja di Chrome 79 dan Firefox 72

Mistermatt
sumber
console.count tidak akan membantu karena penghitung akan disetel ulang setiap kali fungsi dipanggil (seperti yang telah dijelaskan dalam jawaban di atas)
chs242
2
Saya tidak mengerti masalah Anda karena ini berfungsi di Chrome dan Firefox, saya menambahkan cuplikan dalam jawaban saya
Mistermatt
6

Anda dapat menggunakan penutupan untuk ini.

Cukup simpan saja counter ke dalam penutupan fungsi.

Berikut ini contohnya:

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);

Kholiavko
sumber
1
Tetapi dengan cara ini penghitung terus menghitung pada panggilan berikutnya, perlu diatur ulang pada setiap panggilan awal. Ini mengarah ke pertanyaan yang sulit: bagaimana cara mengetahui kapan fungsi rekursif dipanggil dari konteks yang berbeda, dalam hal ini fungsi global vs.
RobG
Ini hanyalah contoh untuk menghasilkan pemikiran. Itu dapat dimodifikasi dengan meminta pengguna untuk kebutuhannya.
Kholiavko
@RobG Saya tidak mengerti pertanyaan Anda. Fungsi rekursif tidak dapat disebut di luar penutupan karena merupakan fungsi bagian dalam. Jadi tidak ada kemungkinan atau kebutuhan untuk membedakan konteks karena hanya ada satu konteks yang mungkin
slebetman
@slebetman Penghitung tidak pernah diatur ulang. Fungsi yang dikembalikan oleh singleDigitDecorator()akan terus menambah penghitung yang sama setiap kali dipanggil.
customcommander
1
@ slebetman — masalahnya adalah bahwa fungsi yang dikembalikan oleh singleDigitDecorator tidak me-reset penghitungnya ketika dipanggil lagi. Itu adalah fungsi yang perlu tahu kapan harus mereset penghitung, jika tidak, instance baru dari fungsi diperlukan untuk setiap penggunaan. Kasing penggunaan yang mungkin untuk Function.caller ? ;-)
RobG
1

Berikut ini adalah versi Python yang menggunakan fungsi wrapper untuk menyederhanakan counter, seperti yang telah disarankan oleh jawaban slebetman - Saya menulis ini hanya karena ide inti sangat jelas dalam implementasi ini:

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)
Luke Sawczak
sumber
1
Dengan Python, saya lebih suka yang seperti ini .
phipsgabler