Bagaimana cara melakukan eksponen di clojure?


106

Bagaimana saya bisa melakukan eksponen di clojure? Untuk saat ini saya hanya membutuhkan eksponen bilangan bulat, tetapi pertanyaannya juga berlaku untuk pecahan.


18
Sebagai seseorang yang tidak tahu clojure, tetapi cenderung menyukainya (menjadi penggemar lisps, pemrograman fungsional, dan memiliki banyak perpustakaan praktis), saya kecewa karena pertanyaan sederhana ini memiliki begitu banyak jawaban - atau itu harus ditanya sama sekali. Saya akan berpikir bahwa eksponen hanya akan menjadi salah satu fungsi dasar yang disediakan tanpa harus melakukan sesuatu yang khusus. Tapi aku senang itu ditanyakan.
Mars

1
baik ya mungkin beberapa versi itu harus menjadi inti ... tetapi saya pikir banyak jawaban masih pertanda baik. "beberapa jalur menuju implementasi" tampaknya menjadi alasan banyak hal ini tidak disediakan - pengguna harus mengetahui detail fungsi yang mereka gunakan demi efisiensi. misalnya (seperti yang ditunjukkan dalam jawaban yang dipilih) beberapa cara berpotensi meledakkan tumpukan, yang lain cenderung tidak melakukannya. mungkin beberapa malas, beberapa bersemangat ... semua detail yang perlu diperhatikan di Clojure, itulah sebabnya saya merasa sebagian besar
lib

3
Saya pikir alasan mengapa tidak hanya fungsi exp di intinya adalah karena menara numerik clojure rusak parah karena alasan efisiensi. Jadi ada berbagai macam hal berbeda yang dapat Anda maksud dengan eksponen. Bagaimana seharusnya (exp 2 (exp 2 200))? Kesalahan atau bilangan bulat besar yang membutuhkan waktu untuk menghitung? Jika Anda hanya ingin exp floating point biasa, maka java sudah ada di dalamnya. Jika Anda ingin bahasa di mana angka melakukan yang terbaik untuk bertindak seperti aslinya, dan menggantung biaya, gunakan skema alih-alih clojure.
John Lawrence Aspden

Jawaban:


141

rekursi klasik (lihat ini, meniup tumpukan)

(defn exp [x n]
     (if (zero? n) 1
         (* x (exp x (dec n)))))

rekursi ekor

(defn exp [x n]
  (loop [acc 1 n n]
    (if (zero? n) acc
        (recur (* x acc) (dec n)))))

fungsional

(defn exp [x n]
  (reduce * (repeat n x)))

licik (juga menumpuk, tapi tidak begitu mudah)

(defn exp-s [x n]
  (let [square (fn[x] (* x x))]
    (cond (zero? n) 1
          (even? n) (square (exp-s x (/ n 2)))
          :else (* x (exp-s x (dec n))))))

Perpustakaan

(require 'clojure.contrib.math)

11
Jawaban yang menghibur!
Matt Fenwick

lihat versi yang sepenuhnya berulang dari solusi licik di bawah stackoverflow.com/a/22977674/231589
Karl Rosaen

5
Clojure.contrib.math sekarang tidak digunakan lagi, lihat Math.Numeric-Tower
alvaro g

Saran kedua (rekursif ekor) memiliki bilangan bulat overflow untuk n <0.
Pointo Senshi

1
Jawaban yang fantastis. Berikut cara melakukannya dengan makro: (defmacro exp [xn] `(* ~ @ (take n (repeat x))))
Daniel Szmulewicz

78

Clojure memiliki fungsi daya yang berfungsi dengan baik: Saya merekomendasikan penggunaan ini daripada melalui interop Java karena ini menangani semua tipe angka presisi arbitrer dengan benar. Itu ada di namespace clojure.math.numeric-tower .

Ini disebut exptuntuk exponentiation daripada poweratau powyang mungkin menjelaskan mengapa itu agak sulit untuk menemukan ... anyway inilah contoh kecil (catatan bahwa usekarya tetapi lebih baik menggunakan require):

(require '[clojure.math.numeric-tower :as math :refer [expt]])  ; as of Clojure 1.3
;; (use 'clojure.contrib.math)     ; before Clojure 1.3
(expt 2 200)
=> 1606938044258990275541962092341162602522202993782792835301376

Pengingat tentang penginstalan paket

Anda harus menginstal paket Java terlebih dahulu org.clojure.math.numeric-toweragar namespace Clojure clojure.math.numeric-towerdapat diakses!

Di baris perintah:

$ lein new my-example-project
$ cd lein new my-example-project

Kemudian edit project.cljdan tambahkan [org.clojure/math.numeric-tower "0.0.4"]ke vektor dependensi.

Mulai REPL lein (bukan REPL clojure)

$ lein repl

Sekarang:

(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16

atau

(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16

1
Mungkin tidak diketahui karena tampaknya bukan bagian dari Clojure standar. 1.3.0 melempar kesalahan tentang tidak dapat menemukan math.clj ketika saya mencoba melakukannya dengan cara ini.
Brian Knoblauch

9
Saya pikir sekarang di "clojure.math.numeric-tower" mulai 1,3 (karena clojure.contrib dipecah menjadi perpustakaan individu)
mikera

Menambahkan "pengingat tentang penginstalan paket" untuk menghilangkan rasa frustrasi pengguna.
David Tonhofer

60

Anda dapat menggunakan java Math.powatau BigInteger.powmetode:

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1, meskipun saya tahu Anda dapat berinteraksi dengan java libs; Namun, 1) Math.pow bekerja dengan ganda, saya butuh Bilangan bulat, dapatkah Anda memberi contoh? 2) Apakah Anda benar-benar harus menggunakan interop untuk sth. sesederhana kekuatan?
Peter

Clojure dibangun di sekitar perpustakaan java, ia tidak berusaha untuk memperbaiki apa yang tidak rusak dan Math / pow berfungsi dengan baik. Mengapa Anda perlu memperhatikan ganda atau bilangan bulat? Anda juga bisa menggunakan richhickey.github.com/clojure-contrib/…
DaVinci

1
@Peter: 1) Kecuali kekuatan Anda begitu besar sehingga tidak dapat secara akurat diwakili oleh ganda lagi, sebenarnya tidak ada masalah dengan hanya mentransmisikan hasilnya ke int. 2) Saya tidak melihat bagaimana menulis Math/powlebih rumit dari math-powatau apapun namanya jika ada padanan jubah. Jika sudah ada metode java sederhana yang melakukan apa yang Anda inginkan, tidak ada alasan untuk membuat ulang fungsionalitas di clojure. Interop Java tidak berbahaya secara inheren.
sepp2k

3
@Da vinci: komentar aneh, ini bahasanya sendiri, dan memiliki banyak fungsi yang ada di Java (seperti stringreverse)
Peter

4
Saya pikir Anda lebih baik menggunakan clojure.contrib.math / expt Clojure jika Anda menginginkan kekuatan bilangan bulat yang akurat. Mungkin melakukan hal yang sama di bawah tenda tetapi jauh lebih baik daripada melalui interop Java .....
mikera


8
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376

6
Anda juga dapat menggunakan notasi literal untuk ini:(.pow 2M 100)
noisesmith

1
Jenisnya berbeda. pengguna => (ketik 2M) java.math.BigDecimal user => (ketik (BigInteger. "2")) java.math.BigInteger
KIM Taegyoon

imo solusi terbaik, showr, menggunakan perpustakaan yang ada, dan termasuk penanganan bigint. +1
Daniel Gruszczyk

Bagi saya yang (Math/pow Math/E x)melakukan trik (mengganti Math/Edengan alas pilihan Anda).
Zaz

6

Jika Anda benar-benar membutuhkan suatu fungsi dan bukan metode, Anda dapat membungkusnya:

 (defn pow [b e] (Math/pow b e))

Dan dalam fungsi ini Anda dapat mentransmisikannya intatau serupa. Fungsi seringkali lebih berguna daripada metode karena Anda dapat meneruskannya sebagai parameter ke fungsi lain - dalam hal ini mapterlintas di benak saya.

Jika Anda benar-benar perlu menghindari interop Java, Anda dapat menulis fungsi daya Anda sendiri. Misalnya, ini adalah fungsi sederhana:

 (defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
   (if (neg? p) (/ 1 result) result)))

Itu menghitung pangkat untuk eksponen integer (yaitu tidak ada akar).

Juga, jika Anda berurusan dengan besar angka, Anda mungkin ingin menggunakan BigIntegerbukan int.

Dan jika Anda berurusan dengan angka yang sangat besar , Anda mungkin ingin mengekspresikannya sebagai daftar digit, dan menulis fungsi aritmatika Anda sendiri untuk mengalirkannya saat mereka menghitung hasil dan mengeluarkan hasilnya ke aliran lain.


4

Saya pikir ini juga akan berhasil:

(defn expt [x pow] (apply * (repeat pow x)))

tetapi tampaknya maksimal pada 2 ^ 63 ... def tidak mampu 2 ^ 200
TJ Trapp

4

SICP menginspirasi versi cepat iteratif penuh dari implementasi 'licik' di atas.

(defn fast-expt-iter [b n]
  (let [inner (fn [a b n]
                (cond
                  (= n 0) a
                  (even? n) (recur a (* b b) (/ n 2))
                  :else (recur (* a b) b (- n 1))))
        ]
    (inner 1 b n)))


2

Penerapan metode "licik" dengan rekursi ekor dan mendukung eksponen negatif:

(defn exp
  "exponent of x^n (int n only), with tail recursion and O(logn)"
   [x n]
   (if (< n 0)
     (/ 1 (exp x (- n)))
     (loop [acc 1
            base x
            pow n]
       (if (= pow 0)
         acc                           
         (if (even? pow)
           (recur acc (* base base) (/ pow 2))
           (recur  (* acc base) base (dec pow)))))))

2

Satu baris sederhana menggunakan pengurangan:

(defn pow [a b] (reduce * 1 (repeat b a)))

1

Mencoba

(defn pow [x n]
  (loop [x x n n r 1]
    (cond
      (= n 0) r
      (even? n) (recur (* x x) (/ n 2) r)
      :else (recur x (dec n) (* r x)))))

untuk solusi tail-recursive O (log n), jika Anda ingin menerapkannya sendiri (hanya mendukung bilangan bulat positif). Jelas, solusi yang lebih baik adalah dengan menggunakan fungsi perpustakaan yang telah ditunjukkan oleh orang lain.


0

Bagaimana dengan clojure.contrib.genric.math-functions

Ada fungsi pow di pustaka clojure.contrib.generic.math-functions. Ini hanyalah makro untuk Math.pow dan lebih merupakan cara "clojureish" untuk memanggil fungsi matematika Java.

http://clojure.github.com/clojure-contrib/generic.math-functions-api.html#clojure.contrib.generic.math-functions/pow


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.