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.
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.
Yanıtlar:
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)
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 power
ya pow
da bulmanın neden biraz zor olduğunu açıklayabilir ... yine de işte küçük bir örnek ( use
iş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
org.clojure.math.numeric-tower
Clojure ad alanını clojure.math.numeric-tower
eriş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.clj
ve 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
Java'nın Math.pow
veya BigInteger.pow
yöntemlerini kullanabilirsiniz :
(Math/pow base exponent)
(.pow (bigint base) exponent)
Math/pow
kadar karmaşık olduğunu math-pow
ya 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.
Bu soru ilk sorulduğunda clojure.contrib.math / expt bunu yapmak için resmi kütüphane işleviydi . O zamandan beri clojure.math.numeric-tower adresine taşındı
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
(Math/pow Math/E x)
için hile yapıyor ( Math/E
seçtiğiniz üs ile değiştirerek ).
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 int
ya da benzerine çevirebilirsiniz . İşlevler genellikle yöntemlerden daha kullanışlıdır çünkü bunları başka işlevlere parametre olarak aktarabilirsiniz - bu durumda map
aklı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 , BigInteger
yerine 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.
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)))
clojure.math.numeric-tower
Eskiden kullanın clojure.contrib.math
.
(ns user
(:require [clojure.math.numeric-tower :as m]))
(defn- sqr
"Uses the numeric tower expt to square a number"
[x]
(m/expt x 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)))))))
İndirgeme kullanan basit bir tek astar:
(defn pow [a b] (reduce * 1 (repeat b a)))
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.
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.