2015-05-19 20 views
9

Risolvendo i problemi del progetto Eulero, ottengo che è necessario effettuare le operazioni con le cifre di un numero lungo normalmente come stringa. Lavoro in linux, emacs, melma con sbcl.Numero intero lungo per stringa e viceversa, operazione con cifre

Ad esempio, per ottenere la somma delle cifre di questo potere 2¹⁰⁰⁰, io lavoro in questo modo,

1) Ottenere il potere

CL-USER> (defparameter *answer-as-integer* (expt 2 1000)) 
*ANSWER-AS-INTEGER* 
CL-USER> *ANSWER-AS-INTEGER* 
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376 

Grazie a Common Lisp questo è molto facile. Ora credo che un buon modo dovrebbe essere quello di applicare ridurre a questa sequenza di cifre.

2) Ottenere la stringa

CL-USER> (defparameter *answer-as-string* (write-to-string *ANSWER-AS-INTEGER*)) 
*ANSWER-AS-STRING* 
CL-USER> *ANSWER-AS-STRING* 
"10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376" 

ora ho la sequenza, in modo da applicare ridurre, ma ho cose sbagliate: Questo è un char quindi applico un char di conversione a intero:

CL-USER> (reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*) 

ma ottengo un errore:

The value 1 is not of type CHARACTER. 
    [Condition of type TYPE-ERROR] 

Restarts: 
0: [RETRY] Retry SLIME REPL evaluation request. 
1: [*ABORT] Return to SLIME's top level. 
2: [ABORT] Abort thread (#<THREAD "repl-thread" RUNNING {1005DE80B3}>) 

Backtrace: 
    0: (DIGIT-CHAR-P 1) [optional] 
    1: ((LAMBDA (X Y)) 1 #\7) 
    2: (REDUCE #<FUNCTION (LAMBDA (X Y)) {100523C79B}> "1071508607186267320948425049060001810561404811705533607443750388370351051124936122493198378815695858127594672917553146825187145285692314043598457757469.. 
    3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (REDUCE (FUNCTION (LAMBDA # #)) *ANSWER-AS-STRING*) #<NULL-LEXENV>) 
    4: (EVAL (REDUCE (FUNCTION (LAMBDA # #)) *ANSWER-AS-STRING*)) 
    5: (SWANK::EVAL-REGION "(reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*) ..) 
     Locals: 
     SB-DEBUG::ARG-0 = "(reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*)\n" 
    6: ((LAMBDA NIL :IN SWANK-REPL::REPL-EVAL)) 
    7: (SWANK-REPL::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) {10051F065B}>) 
    8: (SWANK::CALL-WITH-RETRY-RESTART "Retry SLIME REPL evaluation request." #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) {10051F059B}>) 
    9: (SWANK::CALL-WITH-BUFFER-SYNTAX NIL #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) {10051F057B}>) 
10: (SWANK-REPL::REPL-EVAL "(reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*) ..) 
11: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-REPL:LISTENER-EVAL "(reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*) ..) 
12: (EVAL (SWANK-REPL:LISTENER-EVAL "(reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*) ..) 
13: (SWANK:EVAL-FOR-EMACS (SWANK-REPL:LISTENER-EVAL "(reduce #'(lambda (x y) (+ (digit-char-p x) (digit-char-p y))) *ANSWER-AS-string*) ..) 
14: (SWANK::PROCESS-REQUESTS NIL) 
15: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS)) 
16: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS)) 
17: (SWANK/SBCL::CALL-WITH-BREAK-HOOK #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) {1005DF00DB}>) 
18: ((FLET SWANK/BACKEND:CALL-WITH-DEBUGGER-HOOK :IN "/home/anquegi/quicklisp/dists/quicklisp/software/slime-2.13/swank/sbcl.lisp") #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::H.. 
19: (SWANK::CALL-WITH-BINDINGS ((*STANDARD-OUTPUT* . #1=#<SWANK/GRAY::SLIME-OUTPUT-STREAM {1005DCF343}>) (*STANDARD-INPUT* . #2=#<SWANK/GRAY::SLIME-INPUT-STREAM {1006160003}>) (*TRACE-OUTPUT* . #1#) (*ERR.. 
20: (SWANK::HANDLE-REQUESTS #<SWANK::MULTITHREADED-CONNECTION {1005078BE3}> NIL) 
21: ((FLET #:WITHOUT-INTERRUPTS-BODY-1226 :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE)) 
22: ((FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE)) 
23: ((FLET #:WITHOUT-INTERRUPTS-BODY-647 :IN SB-THREAD::CALL-WITH-MUTEX)) 
24: (SB-THREAD::CALL-WITH-MUTEX #<CLOSURE (FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE) {7FFFEA81ED1B}> #<SB-THREAD:MUTEX "thread result lock" owner: #<SB-THREAD:THR.. 
25: (SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE #<SB-THREAD:THREAD "repl-thread" RUNNING {1005DE80B3}> #S(SB-THREAD:SEMAPHORE :NAME "Thread setup semaphore" :%COUNT 0 :WAITCOUNT 0 :MUTEX #<SB-THREAD:MU.. 
26: ("foreign function: call_into_lisp") 
27: ("foreign function: new_thread_trampoline") 

e se provo ad usare questo come cifre senza conversione del inteerpreter dice che questa Non è un numero intero, quindi sto diventando pazzo, perché questo è giusto, ma il non codice di cui sopra:

(reduce #'+ *ANSWER-AS-string*) 

The value #\1 is not of type NUMBER. 
    [Condition of type TYPE-ERROR] 

Restarts: 
0: [RETRY] Retry SLIME REPL evaluation request. 
1: [*ABORT] Return to SLIME's top level. 
2: [ABORT] Abort thread (#<THREAD "repl-thread" RUNNING {1005DE80B3}>) 

Backtrace: 
    0: (+ #\1 #\0) 
    1: (REDUCE #<FUNCTION +> "107150860718626732094842504906000181056140481170553360744375038837035105112493612249319837881569585812759467291755314682518714528569231404359845775746985748039345677748242309854.. 
    2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (REDUCE (FUNCTION +) *ANSWER-AS-STRING*) #<NULL-LEXENV>) 
    3: (EVAL (REDUCE (FUNCTION +) *ANSWER-AS-STRING*)) 
    4: (SWANK::EVAL-REGION "(reduce #'+ *ANSWER-AS-string*) ..) 
     Locals: 
     SB-DEBUG::ARG-0 = "(reduce #'+ *ANSWER-AS-string*)\n" 
    5: ((LAMBDA NIL :IN SWANK-REPL::REPL-EVAL)) 
    6: (SWANK-REPL::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) {100566384B}>) 
    7: (SWANK::CALL-WITH-RETRY-RESTART "Retry SLIME REPL evaluation request." #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) {100566378B}>) 
    8: (SWANK::CALL-WITH-BUFFER-SYNTAX NIL #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) {100566376B}>) 
    9: (SWANK-REPL::REPL-EVAL "(reduce #'+ *ANSWER-AS-string*) ..) 
10: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-REPL:LISTENER-EVAL "(reduce #'+ *ANSWER-AS-string*) ..) 
11: (EVAL (SWANK-REPL:LISTENER-EVAL "(reduce #'+ *ANSWER-AS-string*) ..) 
12: (SWANK:EVAL-FOR-EMACS (SWANK-REPL:LISTENER-EVAL "(reduce #'+ *ANSWER-AS-string*) ..) 
13: (SWANK::PROCESS-REQUESTS NIL) 
14: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS)) 
15: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS)) 
16: (SWANK/SBCL::CALL-WITH-BREAK-HOOK #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) {1005DF00DB}>) 
17: ((FLET SWANK/BACKEND:CALL-WITH-DEBUGGER-HOOK :IN "/home/anquegi/quicklisp/dists/quicklisp/software/slime-2.13/swank/sbcl.lisp") #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::H.. 
18: (SWANK::CALL-WITH-BINDINGS ((*STANDARD-OUTPUT* . #1=#<SWANK/GRAY::SLIME-OUTPUT-STREAM {1005DCF343}>) (*STANDARD-INPUT* . #2=#<SWANK/GRAY::SLIME-INPUT-STREAM {1006160003}>) (*TRACE-OUTPUT* . #1#) (*ERR.. 
19: (SWANK::HANDLE-REQUESTS #<SWANK::MULTITHREADED-CONNECTION {1005078BE3}> NIL) 
20: ((FLET #:WITHOUT-INTERRUPTS-BODY-1226 :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE)) 
21: ((FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE)) 
22: ((FLET #:WITHOUT-INTERRUPTS-BODY-647 :IN SB-THREAD::CALL-WITH-MUTEX)) 
23: (SB-THREAD::CALL-WITH-MUTEX #<CLOSURE (FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE) {7FFFEA81ED1B}> #<SB-THREAD:MUTEX "thread result lock" owner: #<SB-THREAD:THR.. 
24: (SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE #<SB-THREAD:THREAD "repl-thread" RUNNING {1005DE80B3}> #S(SB-THREAD:SEMAPHORE :NAME "Thread setup semaphore" :%COUNT 0 :WAITCOUNT 0 :MUTEX #<SB-THREAD:MU.. 
25: ("foreign function: call_into_lisp") 
26: ("foreign function: new_thread_trampoline") 

ho risolto questo in questo modo:

CL-USER> (defun sum-digits-in-a-string (str) 
    (reduce #'+ (map 'list #'digit-char-p str))) 
SUM-DIGITS-IN-A-STRING 
CL-USER> (sum-digits-in-a-string *ANSWER-AS-STRING*) 
1366 

Quindi la mia domanda è di questo che ho ottieni questo errore prima un numero intero e dopo un carattere, e qual è il modo migliore di lavorare con le cifre di un intero lungo. Se la mia aproximation è buona: long-integer -> string -> lista di interi -> applica riduci.

risposta

6

Questo non è Common Lisp specifico, ma penso che potrebbe essere un aiuto generale se si inizia aggiungendo un po 'di output di debug alle proprie funzioni. Ad esempio, se si stampano x e y prima di aggiungere e chiamare digit-char-p con essi, è possibile vedere che dopo che i primi due elementi sono stati elaborati, il terzo viene elaborato con il risultato dell'aggiunta precedente, che è un numero, non un personaggio:

CL-USER> (reduce (lambda (x y) 
        (write (list x y)) 
        (+ (digit-char-p x) 
         (digit-char-p y))) 
        "1234") 
(#\1 #\2)(3 #\3) 
; Evaluation aborted on #<TYPE-ERROR expected-type: CHARACTER datum: 3>. 

si può pensare a ciò che uno srotolamento manuale sarà simile:

(+ (digit-char-p (+ (digit-char-p (+ (digit-char-p #\1) 
            (digit-char-p #\2))) 
        (digit-char-p #\3))) 
    (digit-char-p #\4)) 

In quegli scali intermedi, si chiama cifre-char-p su qualcosa che è un numero , non un personaggio.

Hai avuto modo di un buon punto con la soluzione finale:

(defun sum-digits-in-a-string (str) 
    (reduce #'+ (map 'list #'digit-char-p str))) 

ma che presenta il problema spiacevole che si crea una nuova sequenza per contenere le cifre e quindi riduce su di loro. Un altro anti-pattern comune è (applica '+ (map & hellip;)). Il tuo è un po 'meglio, perché usa riduci, ma in realtà riduce un argomento chiave che elimina la necessità della mappa.È possibile utilizzare l'argomento chiave per ridurre per specificare come valori dovrebbero essere estratti dagli elementi della sequenza, ed è possibile utilizzare un valore iniziale (che è importante se la sequenza è vuota, o ha un solo elemento):

CL-USER> (reduce '+ "1234" :key 'digit-char-p :initial-value 0) 
;=> 10 
+0

Non hai nemmeno bisogno di output di debug in più, si può vedere nel backtrace (linea 1). – Svante

+0

@Svante Certo, è anche lì, ma la * pratica * dell'output di debug è utile. (Non sconto i debugger, ecc., Ma un po 'di output di log può fare miracoli.) Inoltre, non è sempre chiaro come un debugger stamperà un valore. Ma hai ragione, in questo caso, l'output era già visibile, ma solo su quella iterazione. Aggiungendo un po 'di output, abbiamo visto anche il primo, dove erano entrambi i personaggi. –

2

Il calcolo eseguito da (reduce f "125") è

(f (f #\1 #\2) #\5) 

Nel vostro caso, questo significa che si sta andando a calcolare

(+ (digit-char-p (+ (digit-char-p #\1) (digit-char-p #\2))) 
    (digit-char-p #\5)) 

quindi sta andando a valutare

(+ (digit-char-p 3) (digit-char-p #\5)) 

che si interrompe con un errore di tipo.

La soluzione più semplice è quello di scrivere una variante di digit-char-p che funziona su interi:

(defun digit-to-int (x) 
    (etypecase x 
    (integer x) 
    (character (digit-char-p x)))) 

(reduce #'(lambda (x y) (+ (digit-to-int x) (digit-to-int y))) *ANSWER-AS-string*) 

Come sottolineato da Joshua Taylor, una soluzione più pulita è quella di utilizzare il parametro :key-reduce, che è più o meno equivalente a utilizzando map ma evita la necessità di generare una sequenza di intermediario:

(reduce #'+ *ANSWER-AS-string* :key #'digit-char-p) 
5

si può evitare di stampare il numero in una stringa e generare un elenco di cifre decimali direttamente.

esplodere e implodere

Di solito questa operazione si chiama esplodere. In genere l'operazione di esplosione può trattare con simboli, numeri interi e simili. Crea una lista dei componenti. Il funzionamento inverso è chiamato implode.

Questo sarebbe esplodere per gli interi positivi:

(defun explode-integer (integer) 
    "Explode a positve integer." 
    (labels ((aux-explode-integer (integer) 
      (nreverse 
       (loop with i = integer and r = 0 
        while (> i 0) 
        do (multiple-value-setq (i r) (floor i 10)) 
        collect r)))) 
    (cond ((plusp integer) 
      (aux-explode-integer integer)) 
      ((zerop integer) (list 0))))) 

Esempio:

CL-USER 31 > (explode-integer 572304975029345020734) 
(5 7 2 3 0 4 9 7 5 0 2 9 3 4 5 0 2 0 7 3 4) 
Problemi correlati