Làm cách nào tôi có thể tính lũy thừa trong clojure? Bây giờ tôi chỉ cần lũy thừa số nguyên, nhưng câu hỏi cũng dành cho phân số.
Làm cách nào tôi có thể tính lũy thừa trong clojure? Bây giờ tôi chỉ cần lũy thừa số nguyên, nhưng câu hỏi cũng dành cho phân số.
Câu trả lời:
đệ quy cổ điển (xem này, nó thổi chồng lên)
(defn exp [x n]
(if (zero? n) 1
(* x (exp x (dec n)))))
đệ quy đuôi
(defn exp [x n]
(loop [acc 1 n n]
(if (zero? n) acc
(recur (* x acc) (dec n)))))
chức năng
(defn exp [x n]
(reduce * (repeat n x)))
lén lút (cũng có thể thổi stack, nhưng không dễ dàng như vậy)
(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))))))
thư viện
(require 'clojure.contrib.math)
Clojure có một hàm nguồn hoạt động tốt: Tôi khuyên bạn nên sử dụng hàm này thay vì sử dụng hàm tương tác Java vì nó xử lý chính xác tất cả các loại số có độ chính xác tùy ý của Clojure. Nó nằm trong không gian tên clojure.math.numeric-tower .
Nó được gọi là expt
cho lũy thừa chứ không phải là power
hay pow
mà có lẽ giải thích tại sao đó là một chút khó khăn để tìm ... anyway đây là một ví dụ nhỏ (lưu ý rằng use
tác phẩm nhưng sử dụng tốt hơ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
Trước tiên, bạn phải cài đặt gói Java org.clojure.math.numeric-tower
để làm cho không gian tên Clojure clojure.math.numeric-tower
có thể truy cập được!
Trên dòng lệnh:
$ lein new my-example-project
$ cd lein new my-example-project
Sau đó, chỉnh sửa project.clj
và thêm [org.clojure/math.numeric-tower "0.0.4"]
vào vector phụ thuộc.
Bắt đầu REPL lein (không phải REPL áo choàng)
$ lein repl
Hiện nay:
(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16
hoặc là
(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
Bạn có thể sử dụng java Math.pow
hoặc BigInteger.pow
các phương pháp:
(Math/pow base exponent)
(.pow (bigint base) exponent)
Math/pow
phức tạp hơn math-pow
hoặc bất cứ cái tên nào nếu có một chiếc áo choàng tương đương. Nếu đã có một phương thức java đơn giản thực hiện những gì bạn muốn, thì không có lý do gì để tạo lại chức năng trong clojure. Java interop vốn dĩ không có hại.
Khi câu hỏi này ban đầu được hỏi, clojure.contrib.math / expt là chức năng thư viện chính thức để thực hiện điều này. Kể từ đó, nó đã chuyển sang clojure.math.numeric-tower
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
(Math/pow Math/E x)
thực hiện thủ thuật (thay thế Math/E
bằng cơ sở của sự lựa chọn của bạn).
Nếu bạn thực sự cần một hàm chứ không phải một phương thức, bạn có thể chỉ cần gói nó:
(defn pow [b e] (Math/pow b e))
Và trong chức năng này, bạn có thể truyền nó đến int
hoặc tương tự. Các hàm thường hữu ích hơn các phương thức vì bạn có thể truyền chúng dưới dạng tham số cho các hàm khác - trong trường hợp map
này tôi nghĩ đến.
Nếu bạn thực sự cần tránh tương tác Java, bạn có thể viết hàm nguồn của riêng mình. Ví dụ, đây là một hàm đơn giản:
(defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
(if (neg? p) (/ 1 result) result)))
Điều đó tính toán lũy thừa cho số mũ nguyên (tức là không có căn).
Ngoài ra, nếu bạn đang xử lý số lượng lớn , bạn có thể muốn sử dụng BigInteger
thay thế int
.
Và nếu bạn đang xử lý các số rất lớn , bạn có thể muốn biểu thị chúng dưới dạng danh sách các chữ số và viết các hàm số học của riêng bạn để truyền qua chúng khi chúng tính toán kết quả và xuất kết quả ra một số luồng khác.
SICP đã lấy cảm hứng từ phiên bản nhanh chóng lặp đi lặp lại đầy đủ của việc triển khai 'lén lút' ở trên.
(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)))
Sử dụng clojure.math.numeric-tower
, trước đây 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))
Thực hiện phương pháp "lén lút" với đệ quy đuôi và hỗ trợ số mũ âm:
(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)))))))
Một lớp lót đơn giản sử dụng giảm:
(defn pow [a b] (reduce * 1 (repeat b a)))
Thử
(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)))))
đối với giải pháp O (log n) đệ quy đuôi, nếu bạn muốn tự thực hiện (chỉ hỗ trợ số nguyên dương). Rõ ràng, giải pháp tốt hơn là sử dụng các hàm thư viện mà những người khác đã chỉ ra.
Còn về clojure.contrib.genric.math-functions
Có một hàm pow trong thư viện clojure.contrib.generic.math-functions. Nó chỉ là một macro đối với Math.pow và giống như một cách gọi hàm Java toán học theo kiểu "sơ sài".