Continuând tema mea de a învăța toate limbile, am profitat de un puzzle de programare pentru a încerca aceeași abordare într-o mână de limbi diferite pentru a compara modul în care funcționează.
Pentru o viitoare întâlnire a APL, provocarea a fost pusă așa cum a fost prezentată la sfârșitul acestei postări, și anume
Mi se pare distractiv – și am vrut să văd cum ar putea arăta soluțiile în unele dintre diferitele limbi pe care le cunosc (inclusiv un APL, de dragul întâlnirii viitoare).
Am ajuns să folosesc R, (Dyalog) APL, Julia, Haskell, Python și Rust; cineva a oferit o soluție J; și voi adăuga în orice altele împărtășite cu mine. Site-ul de mai sus a colectat soluții Clojure în acest esențial.
Iată soluțiile mele în fiecare limbă; nu este atât de mult pentru comparație una lângă alta, dar poți comuta între cele diferite. Setul complet de fișiere este aici dacă sunteți interesat.
R
Sunt cel mai familiarizat cu R, așa că îmi place să încep de acolo. Am creat un swap
funcție care schimbă un vector la niște indici, împreună cu niște ajutoare, astfel încât să pot folosi pmap_int() cu adevărat curat.
swap <- function(x, y, v) {
xx <- v(x)
yy <- v(y)
v(x) <- yy
v(y) <- xx
v
}
chr_swap <- function(x, y, v) {
paste0(swap(x, y, v), collapse = "")
}
toInt_swap <- function(x, y, v) {
as.integer(chr_swap(x, y, v))
}
maxmin <- function(num) {
chars <- strsplit(as.character(num), "")((1))
n <- nchar(num)
s <- seq_len(n)
opts <- expand.grid(x = s, y = s)
opts$v <- list(chars)
vals <- purrr::pmap_int(opts, toInt_swap)
keeps <- vals(nchar(vals) == n)
c(max(keeps), min(keeps))
}
maxmin(213)
(1) 312 123
maxmin(12345)
(1) 52341 12345
maxmin(100)
(1) 100 100
maxmin(11321)
(1) 31121 11123
The expand.grid() creează unele combinații redundante, dar acestea cad în mod natural, așa că nu m-am obosit să le filtrez. De asemenea, deoarece aceasta include schimburile fără operațiuni (de exemplu, indicele de schimb 2 şi 2) conține deja vectorul original. Mai degrabă decât filtrarea la vectorii numerelor întregi nu
începând cu 0, am filtrat la cele care conțin numărul corect de cifre după convertirea înapoi în întreg, care este echivalent.
Încercați să lipiți codul în editorul online {webr} aici; Nu sunt sigur dacă este posibil să se conecteze la o stare existentă, dar când vă întreabă dacă doriți să instalați {purrr} în interfață, răspundeți că o faceți.
APL

În Dyalog APL este mai ușor să definiți o funcție de swap; cel @ operatorul ia o funcție (invers) deci s aici efectuează un schimb. Produsul exterior este super la îndemână pentru a găsi toate combinațiile de x şi y: x ∘., y.
maxmin←{
⎕IO←1 ⍝ so that x(1) is subset not x(0)
n←⍎¨⍕⍵ ⍝ convert int to vec
s←{⌽@⍵⊢⍺} ⍝ swap two elements
swaps←{n s ⍵} ⍝ apply swaps to a vec n
opts←,(⍳≢n)∘.,⍳≢n ⍝ combinations of 1..n
new←swaps¨opts ⍝ perform the swaps
keep←(~0=⊃¨new)/new ⍝ filter out values starting with 0
(⌈/,⌊/)10⊥¨keep ⍝ max and min of ints
}
maxmin 213
312 123
maxmin 12345
52341 12345
maxmin 100
100 100
maxmin 11321
31121 11123
Sunt destul de multumit de aceasta solutie; efectuând a map este la fel de simplu ca să folosești fiecare (¨) și efectuând ambele max şi min concatenate împreună cu o furculiță ((⌈/,⌊/)) este atât de estetic. Conversia dintr-un vector de numere într-un singur număr utilizează o decodare în bază 10 (10⊥) care este modul în care ar putea fi necesar să faceți asta în alte limbi, dar cu o buclă.
Dacă ar fi să îmi iau niște libertăți cu ceea ce se numește „linie”, aș putea spune că aceasta este o soluție cu o linie.
maxmin←{⎕IO←1 ⋄ n←⍎¨⍕⍵ ⋄ s←{⌽@⍵⊢⍺} ⋄ swaps←{n s ⍵} ⋄ opts←,(⍳≢n)∘.,⍳≢n ⋄ new←swaps¨opts ⋄ keep←(~0=⊃¨new)/new ⋄ (⌈/,⌊/)10⊥¨keep }
Puteți încerca acest lucru singur la tryapl.org
Julia

În Julia swap funcția poate folosi destructurarea, ceea ce este drăguț, dar, deoarece limbajul folosește semantică de trecere prin referință, trebuie să fac o copie a vectorului care este schimbat, altfel voi continua să-l schimb mereu și iar. Notă: această postare recentă a mea.
using Combinatorics
function swap(x, i, j)
y = copy(x)
y(i), y(j) = y(j), y(i)
y
end
function maxmin(x)
nvec = parse.(Int64, split(string(x), ""))
opts = collect(combinations(1:length(nvec), 2))
new = ((nvec); map(x -> swap(nvec, x...), opts))
keep = filter(x -> x(1) != 0, new)
vals = parse.(Int64, join.(keep))
(maximum(vals), minimum(vals))
end
maxmin(213)
(312, 123)
maxmin(12345)
(52341, 12345)
maxmin(100)
(100, 100)
maxmin(11321)
(31121, 11123)
Partea cu care am avut probabil cele mai multe probleme aici a fost concatenarea vectorului original cu versiunile lui schimbate; acum pare curat, dar mi-a luat ceva timp să-mi dau seama cum să le aduc pe toate în același vector-de-vectori.
Stropirea de opts variante în map a fost drăguț; nu este nevoie să definiți schimbul în termeni de tuplu. În general, aceasta este o soluție foarte curată, după părerea mea – Julia face într-adevăr un limbaj minunat.
Haskell

Continuând călătoria mea de învățare Haskell, m-am gândit că cel mai bine ar fi să încerc asta. Fiind un limbaj puternic funcțional, nu se definește o mulțime de variabile, în schimb se scrie o mulțime de funcții care vor transmite date. Acest lucru îl face puțin dificil pentru testare, dar am ajuns acolo în cele din urmă. A trebuit să împrumut swapElts funcția și nub a fost una nouă pentru mine (în esență unique()).
import Data.List
import Data.Digits
uniq_pairs l = nub ((x,y) | x <- l, y <- l, x < y)
opts n = uniq_pairs (0..n-1)
-- https://gist.github.com/ijt/2010183
swapElts i j ls = (get k x | (k, x) <- zip (0..length ls - 1) ls)
where get k x | k == i = ls !! j
| k == j = ls !! i
| otherwise = x
doswap t v = swapElts (fst t) (snd t) v
newlist v = v : map (x -> doswap x v) (opts (length v))
keep v = filter (x -> (head x /= 0)) (newlist v)
maxmin n = (maximum(x), minimum(x)) where
x = map (unDigits 10) (keep (digits 10 n))
maxmin 213
(312,123)
maxmin 12345
(52341,12345)
maxmin 100
(100,100)
maxmin 11321
(31121,11123)
The Data.Digits pachetul a fost foarte util aici – având digits şi
unDigitsdeși dacă aveam de gând să le folosesc mai mult, aș fi transformat baza 10 necesară în ceva de genul digits10 şi unDigits10.
Sunt posibile îmbunătățiri care trebuie făcute aici și sunt interesat de orice puteți observa!
Piton

„Toată lumea” îl folosește, așa că trebuie să învăț… este ceea ce îmi tot spun. Nu sunt străin de ciudateniile diferitelor limbi, dar de fiecare dată când încerc să fac ceva funcțional în python ajung să fiu supărat că metoda de imprimare pentru generatoare arată adresa de memorie în loc, să zicem, de primele câteva elemente. Imprimarea unei valori și vedea
mă primește fiecare. singur. timp. Da, da, list(value) îl „colectează”, dar grrr…
Python are sintaxa de destructurare care este drăguță în swap funcția, dar din nou este trece prin referință, așa că trebuie să fac mai întâi o copie.
import itertools
def swap(x, t):
y = x.copy()
i, j = t
y(i), y(j) = y(j), y(i)
return y
def minmax(num):
nums = (int(i) for i in str(num))
opts = itertools.combinations(range(len(nums)), 2)
new = map(lambda x: swap(nums, x), list(opts))
keeps = list(filter(lambda x: x(0) != 0, new))
keeps.append(nums)
vals = list(map(lambda x: int(''.join(map(str, x))), keeps))
return (max(vals), min(vals))
minmax(213)
(312, 123)
minmax(12345)
(52341, 12345)
minmax(100)
(100, 100)
minmax(11321)
(31121, 11123)
În afară de mormăielile mele în timp ce scriu, soluția este încă destul de curată. Apelurile către list() intercalate de-a lungul ar putea fi evitată, dar nevoia de a face asta în timp ce mă dezvoltam măcar m-a încetinit.
Rugini

Aproape că nu am făcut o soluție Rust pentru că am crezut că am făcut destul. A ajuns să fie cel mai complicat, totuși – nu sunt sigur dacă asta este din cauza mea sau a lui Rust.
use itertools::Itertools; fn swap(v: Vec, t1: usize, t2: usize) -> Vec { let mut vv = v; let tmp1 = vv(t1); let tmp2 = vv(t2); vv(t1) = tmp2; vv(t2) = tmp1; return vv; } fn maxmin(num: u32) -> (u32, u32) { let numc = num.to_string(); let n = numc.len(); let numv: Vec = numc .to_string() .chars() .map(|c| c.to_digit(10).unwrap()) .collect(); let mut opts = Vec::new(); for (a, b) in (0..n).tuple_combinations() { opts.push((a, b)); } let mut new: Vec > = Vec::new(); new.push(numv.clone()); for o in opts { new.push(swap(numv.clone(), o.0, o.1)); } let keeps: Vec > = new.into_iter().filter(|x| x(0) != 0).collect(); let mut vals = Vec::new(); for v in keeps { let tmp: u32 = v .clone() .into_iter() .map(|x| x.to_string()) .collect:: () .parse() .unwrap(); vals.push(tmp); } let min = *vals.iter().min().unwrap(); let max = *vals.iter().max().unwrap(); (max, min) } fn main() { println!("{:?}", maxmin(213)); println!("{:?}", maxmin(12345)); println!("{:?}", maxmin(100)); println!("{:?}", maxmin(11321)) } (312, 123) (52341, 12345) (100, 100) (31121, 11123)
Această soluție mi-a reamintit de ce îmi place să lucrez cu limbaje matrice (sau cel puțin care suportă vectori); nu este nevoie să bucleți în mod explicit peste fiecare element al unui vector pentru a face ceva. A trebuit să scriu multe push()
bucle pentru a muta datele. max() nu funcționează pe un vector (în sensul găsirii maximului de n elemente); funcționează așa pe un iterator și poate eșua, deci cu atât mai mult min şi max linii.
A trebuit clone() diverse valori în mod explicit pentru că nu pot fi reutilizate a fost puțin enervant, dar înțeleg de ce se plânge de acestea.
Acest lucru a durat mai mult decât mi-aș fi dorit, dar bineînțeles că am învățat mai multe făcând-o.
J

La întâlnirea APL am discutat despre o soluție J parțială care a folosit o abordare ușor diferită a algoritmului de „swap”. Nu sunt sigur că acolo este un mod în J care este la fel de elegant ca soluția APL, dar m-ar interesa dacă există.
Justus Perlwitz a oferit această soluție, a cărei esență este
digits =: 10.^:_1
sd =: {{
amend =. (|.y)}
swap =. (y { )) amend )
swap &.: digits x
}}
cart =: {{
all =. ,/ (,"0)/~ y
uniq =. ~. /:~"1 all
l =. 0{"1 uniq
r =. 1{"1 uniq
(l ~: r) # uniq
}}
swapmaxmin =: {{
ndigits =. (: # digits
combs =. cart i. ndigits y
constr =. ((ndigits y) <: (: ndigits"0 )) # )
swaps =. constr y, y sd"1 combs
(>./ , <./) swaps
}}
swapmaxmin 213
312 123
swapmaxmin 12345
52341 12345
swapmaxmin 100
100 100
swapmaxmin 11321
31121 11123
și pe care o poți alerga în locul de joacă J
Vreau să învăț multe despre J, așa că voi cerceta și eu această soluție.
Rezumat
Am fost cel mai multumit de solutia APL; face ceea ce scrie pe cutie fără ambiguitate, deoarece este construit în întregime din primitive (sau funcții de utilitate definite în termenii acestora). Soluția Julia se simte, de asemenea, foarte curată, în timp ce soluția Haskell, definită în întregime din funcții, demonstrează frumos principiul funcțional.
Am găsit că este un exemplu interesant de unde este trecerea prin referință nu atât de util. Pentru funcțiile Julia pachetate, această distincție este clarificată cu
! sufix pentru a desemna funcțiile mutante și este obișnuit să scrieți atât o versiune mutantă, cât și o versiune nemutant ori de câte ori este posibil.
Scrierea acestora m-a învățat din ce în ce mai multe despre utilizarea fiecăreia dintre aceste limbi și sunt de părere că doar citirea soluțiilor nu înlocuiește să vă murdărească mâinile cu un cod real.
Comentariile, îmbunătățirile sau propriile soluții sunt binevenite. Pot fi găsit pe Mastodon sau folosesc comentariile de mai jos.
devtools::session_info()
## ─ Session info ─────────────────────────────────────────────────────────────── ## setting value ## version R version 4.4.1 (2024-06-14) ## os macOS Sonoma 14.6 ## system aarch64, darwin20 ## ui X11 ## language (EN) ## collate en_US.UTF-8 ## ctype en_US.UTF-8 ## tz Australia/Adelaide ## date 2024-10-26 ## pandoc 3.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown) ## ## ─ Packages ─────────────────────────────────────────────────────────────────── ## package * version date (UTC) lib source ## blogdown 1.19 2024-02-01 (1) CRAN (R 4.4.0) ## bookdown 0.41 2024-10-16 (1) CRAN (R 4.4.1) ## bslib 0.8.0 2024-07-29 (1) CRAN (R 4.4.0) ## cachem 1.1.0 2024-05-16 (1) CRAN (R 4.4.0) ## cli 3.6.3 2024-06-21 (1) CRAN (R 4.4.0) ## devtools 2.4.5 2022-10-11 (1) CRAN (R 4.4.0) ## digest 0.6.37 2024-08-19 (1) CRAN (R 4.4.1) ## ellipsis 0.3.2 2021-04-29 (1) CRAN (R 4.4.0) ## evaluate 1.0.1 2024-10-10 (1) CRAN (R 4.4.1) ## fastmap 1.2.0 2024-05-15 (1) CRAN (R 4.4.0) ## fs 1.6.4 2024-04-25 (1) CRAN (R 4.4.0) ## glue 1.8.0 2024-09-30 (1) CRAN (R 4.4.1) ## htmltools 0.5.8.1 2024-04-04 (1) CRAN (R 4.4.0) ## htmlwidgets 1.6.4 2023-12-06 (1) CRAN (R 4.4.0) ## httpuv 1.6.15 2024-03-26 (1) CRAN (R 4.4.0) ## jquerylib 0.1.4 2021-04-26 (1) CRAN (R 4.4.0) ## jsonlite 1.8.9 2024-09-20 (1) CRAN (R 4.4.1) ## knitr 1.48 2024-07-07 (1) CRAN (R 4.4.0) ## later 1.3.2 2023-12-06 (1) CRAN (R 4.4.0) ## lifecycle 1.0.4 2023-11-07 (1) CRAN (R 4.4.0) ## magrittr 2.0.3 2022-03-30 (1) CRAN (R 4.4.0) ## memoise 2.0.1 2021-11-26 (1) CRAN (R 4.4.0) ## mime 0.12 2021-09-28 (1) CRAN (R 4.4.0) ## miniUI 0.1.1.1 2018-05-18 (1) CRAN (R 4.4.0) ## pkgbuild 1.4.4 2024-03-17 (1) CRAN (R 4.4.0) ## pkgload 1.4.0 2024-06-28 (1) CRAN (R 4.4.0) ## profvis 0.4.0 2024-09-20 (1) CRAN (R 4.4.1) ## promises 1.3.0 2024-04-05 (1) CRAN (R 4.4.0) ## purrr 1.0.2 2023-08-10 (1) CRAN (R 4.4.0) ## R6 2.5.1 2021-08-19 (1) CRAN (R 4.4.0) ## Rcpp 1.0.13 2024-07-17 (1) CRAN (R 4.4.0) ## remotes 2.5.0 2024-03-17 (1) CRAN (R 4.4.0) ## rlang 1.1.4 2024-06-04 (1) CRAN (R 4.4.0) ## rmarkdown 2.28 2024-08-17 (1) CRAN (R 4.4.0) ## rstudioapi 0.17.0 2024-10-16 (1) CRAN (R 4.4.1) ## sass 0.4.9 2024-03-15 (1) CRAN (R 4.4.0) ## sessioninfo 1.2.2 2021-12-06 (1) CRAN (R 4.4.0) ## shiny 1.9.1 2024-08-01 (1) CRAN (R 4.4.0) ## urlchecker 1.0.1 2021-11-30 (1) CRAN (R 4.4.0) ## usethis 3.0.0 2024-07-29 (1) CRAN (R 4.4.0) ## vctrs 0.6.5 2023-12-01 (1) CRAN (R 4.4.0) ## xfun 0.48 2024-10-03 (1) CRAN (R 4.4.1) ## xtable 1.8-4 2019-04-21 (1) CRAN (R 4.4.0) ## yaml 2.3.10 2024-07-26 (1) CRAN (R 4.4.0) ## ## (1) /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library ## ## ──────────────────────────────────────────────────────────────────────────────
