Clojure'de üs alma nasıl yapılır?


106

Clojure'de üs alma işlemini nasıl yapabilirim? Şimdilik sadece tamsayı üssüne ihtiyacım var ama soru kesirler için de geçerli.


18
Clojure'ı bilmeyen, ancak onu sevmeye yatkın biri olarak (pelteklik hayranı olmak, işlevsel programlama ve çok sayıda kullanışlı kitaplığa sahip olmak), bu basit sorunun bu kadar çok cevabı olduğu için hayal kırıklığına uğradım. hiç sorulmalıydı. Üstelleştirmenin, özel bir şey yapmak zorunda kalmadan sağlanan temel işlevlerden biri olacağını düşünürdüm. Yine de sorulduğuna sevindim.
Mars

1
evet muhtemelen onun bir versiyonu çekirdekte olmalı ... ama birçok cevabın hala iyi bir işaret olduğunu düşünüyorum. "Uygulamaya giden çoklu yollar" bu şeylerin çoğunun sağlanmamasının nedeni gibi görünüyor - kullanıcı, verimlilik uğruna kullandıkları işlevin ayrıntılarını bilmelidir. örneğin (seçilen cevapta belirtildiği gibi) bazı yollar potansiyel olarak yığını patlatabilir, diğerleri ise daha az olasıdır. belki bazıları tembel, bazıları heveslidir ...
Clojure'da

3
Bence çekirdekte sadece bir exp işlevi olmamasının nedeni, clojure'un sayısal kulesinin verimlilik nedenlerinden ötürü kötü bir şekilde kırılmış olmasıdır. Öyleyse, üs alma ile kastettiğin birçok farklı şey var. (Exp 2 (exp 2 200)) ne olmalı? Hesaplaması bir yaş alan bir hata mı yoksa büyük bir tam sayı mı? Yalnızca normal kayan nokta exp'ını istiyorsanız, o zaman java yerleşiktir. Sayıların gerçek gibi davranıp maliyeti asmak için ellerinden gelenin en iyisini yaptığı bir dil istiyorsanız, clojure yerine şema kullanın.
John Lawrence Aspden

Yanıtlar:


141

klasik özyineleme (bunu izle, yığını patlatır)

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

kuyruk özyineleme

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

işlevsel

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

sinsi (aynı zamanda yığını da üfler, ancak o kadar kolay değil)

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

kütüphane

(require 'clojure.contrib.math)

11
Eğlenceli cevap!
Matt Fenwick

Aşağıda sinsi çözümün tam iteratif halini görmek stackoverflow.com/a/22977674/231589
Karl Rosaen

5
Clojure.contrib.math artık kullanımdan kaldırıldı, bkz. Math.Numeric-Tower
alvaro g

İkinci öneride (kuyruk özyinelemeli) n <0 için tamsayı taşması var.
Pointo Senshi

1
Harika cevap. Bunu bir makro ile nasıl yapacağınız aşağıda açıklanmıştır: (defmacro exp [xn] `(* ~ @ (n (tekrar x))))
Daniel Szmulewicz

78

Clojure'un iyi çalışan bir güç işlevi vardır: Tüm Clojure rasgele kesinlikli sayı türlerini doğru şekilde işlediği için Java ile birlikte çalışmak yerine bunu kullanmanızı öneririm. Clojure.math.numeric-tower ad alanında .

Bu expt, üs alma yerine powerya powda bulmanın neden biraz zor olduğunu açıklayabilir ... yine de işte küçük bir örnek ( useişe yaradığını ancak daha iyi kullanıldığını unutmayın 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

Paket kurulumuyla ilgili hatırlatma

org.clojure.math.numeric-towerClojure ad alanını clojure.math.numeric-towererişilebilir hale getirmek için önce Java paketini yüklemelisiniz !

Komut satırında:

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

Ardından , bağımlılıklar vektörünü düzenleyin project.cljve ekleyin [org.clojure/math.numeric-tower "0.0.4"].

Bir lein REPL başlatın (clojure REPL değil)

$ lein repl

Şimdi:

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

veya

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

1
Muhtemelen bilinmiyor çünkü standart Clojure'un bir parçası gibi görünmüyor. 1.3.0, bu şekilde yapmaya çalıştığımda math.clj dosyasını bulamama hatalarını fırlatıyor.
Brian Knoblauch

9
Sanırım şu anda 1.3 itibarıyla "clojure.math.numeric-tower" içinde (clojure.contrib bireysel kitaplıklara ayrıldığından beri)
mikera

Kullanıcı hayal kırıklığını gidermek için "paket kurulumu hakkında hatırlatma" eklendi.
David TONHOFER

60

Java'nın Math.powveya BigInteger.powyöntemlerini kullanabilirsiniz :

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1, java libs ile birlikte çalışabileceğinizi biliyorum; 1) Math.pow çiftlerle çalışır, Tamsayılara ihtiyacım var, bir örnek verebilir misiniz? 2) sth için gerçekten interop kullanmanız gerekiyor mu? güçler kadar basit mi?
Peter

Clojure java kitaplıklarının çevresinde inşa edilmiştir, bozuk olmayanları onarmaya çalışmaz ve Math / pow gayet iyi çalışır. Neden çiftlere veya tam sayılara dikkat etmeniz gerekiyor? Ayrıca bu richhickey.github.com/clojure-contrib/… 'i
DaVinci

1
@Peter: 1) Güçleriniz artık çiftlerle doğru bir şekilde temsil edilemeyecek kadar büyük olmadıkça, sonucun int'e aktarılmasında gerçekten bir sorun yok. 2) Yazmanın ne Math/powkadar karmaşık olduğunu math-powya da ad ne olursa olsun eşdeğeri bir taklit olsaydı anlamıyorum . İstediğinizi yapan basit bir java yöntemi zaten varsa, clojure'de işlevselliği yeniden oluşturmak için hiçbir neden yoktur. Java birlikte çalışması, doğası gereği zararlı değildir.
sepp2k

3
@Da Vinci: garip sözler, kendi dilidir ve Java'da (stringreverse gibi) birçok işlevi vardır
Peter

4
Doğru biginteger güçleri istiyorsanız Clojure'un clojure.contrib.math / expt'ı kullanmanız daha iyi olur. Muhtemelen aynısını yapıyor ama Java ile birlikte çalışmaktan çok daha iyi .....
mikera


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

6
ayrıca bunun için birebir gösterimi de kullanabilirsiniz:(.pow 2M 100)
noisesmith

1
Türleri farklı. user => (2M yazın) java.math.BigDecimal user => (type (BigInteger. "2")) java.math.BigInteger
KIM Taegyoon

imo best solution, showr, mevcut kütüphaneleri kullanarak ve bigint'in işlenmesi dahil. +1
Daniel Gruszczyk

Benim (Math/pow Math/E x)için hile yapıyor ( Math/Eseçtiğiniz üs ile değiştirerek ).
Zaz

6

Bir yönteme değil de gerçekten bir işleve ihtiyacınız varsa, onu kolayca sarmalayabilirsiniz:

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

Ve bu işlevde onu intya da benzerine çevirebilirsiniz . İşlevler genellikle yöntemlerden daha kullanışlıdır çünkü bunları başka işlevlere parametre olarak aktarabilirsiniz - bu durumda mapaklıma geliyor.

Java birlikte çalışmasından gerçekten kaçınmanız gerekiyorsa, kendi güç işlevinizi yazabilirsiniz. Örneğin, bu basit bir işlevdir:

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

Bu, tamsayı üssü için gücü hesaplar (yani kök yok).

Ayrıca, büyük sayılarla uğraşıyorsanız , BigIntegeryerine kullanmak isteyebilirsiniz int.

Ve çok büyük sayılarla uğraşıyorsanız , bunları basamak listeleri olarak ifade etmek ve sonucu hesaplarken ve sonucu başka bir akışa çıkarırken üzerlerine akış yapmak için kendi aritmetik işlevlerinizi yazmak isteyebilirsiniz.


4

Bunun da işe yarayacağını düşünüyorum:

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

ancak 2 ^ 63'te maksimuma çıkıyor gibi görünüyor ... def 2 ^ 200 kapasitesine sahip değil
TJ Trapp

4

SICP , yukarıdaki 'sinsi' uygulamanın tam yinelemeli hızlı versiyonundan ilham almıştır .

(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

Kuyruk özyinelemeli ve negatif üs destekli "sinsi" yöntemin uygulanması:

(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

İndirgeme kullanan basit bir tek astar:

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

1

Deneyin

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

kuyruk özyinelemeli bir O (log n) çözümü için, bunu kendiniz uygulamak istiyorsanız (yalnızca pozitif tam sayıları destekler). Açıktır ki, daha iyi çözüm, başkalarının işaret ettiği kütüphane işlevlerini kullanmaktır.


0

Clojure.contrib.genric.math-functions nasıl

Clojure.contrib.generic.math-functions kütüphanesinde bir pow işlevi vardır. Bu sadece Math.pow için bir makrodur ve Java matematik fonksiyonunu çağırmanın daha "tıkanık" bir yoludur.

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


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.