This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-08-30
Channels
- # aleph (15)
- # announcements (4)
- # aws (2)
- # bangalore-clj (7)
- # beginners (236)
- # calva (24)
- # cider (11)
- # cljs-dev (63)
- # clojure (141)
- # clojure-europe (3)
- # clojure-india (2)
- # clojure-italy (8)
- # clojure-nl (3)
- # clojure-spec (8)
- # clojure-uk (52)
- # clojured (1)
- # clojuredesign-podcast (4)
- # clojurescript (35)
- # clojutre (3)
- # community-development (1)
- # cursive (77)
- # data-science (1)
- # datomic (3)
- # emacs (13)
- # fulcro (7)
- # graalvm (78)
- # graphql (2)
- # nrepl (7)
- # off-topic (18)
- # pathom (25)
- # reagent (12)
- # reitit (31)
- # shadow-cljs (178)
- # spacemacs (7)
- # tools-deps (32)
- # xtdb (10)
- # yada (3)
most of those are not falsey
looks like you want #(or (not %) (zero? %) (and (coll? %) (empty? %)))
there's nothing built in that combines those
If you want nil
and false
to be considered logical false, and every other value logical true, as Clojure if
and cond
do, for example, you could use boolean
as a predicate.
Thanks, @noisesmith 👍
I wanted to try our clojurescript for the first time because I love clojure. For the last 4 hours I tried to get a minimal working example of a cljs/reagent app to run. I used all kinds of tutorials in blog posts or on YouTube. None of them worked, most of them did not even get me to a working repl, aborting with a 300 line long stack trace after downloading stuff for 4 minutes. I have tried chestnut, figwheel both with and without leiningen. Could someone be so kind to recommend an up-to-date guide/tutorial to set up a minimal reagent app? After failing miserably for the last 4 hours that would be very much appreciated. 🙂
I think the luminus template for clj/cljs is the most thoroughly documented and up to date right now
but someone else might have fresher info
@tilman.schieber I had success with the free "learn reagent" tutorial, not sure if that's one of the ones you tried. https://www.learnreagent.com/
I have not tried these two, thanks for the tips. Has so much changed in the last years that all older tutorials are deprecated?
No idea, I have been interested in clojure for years but have only started my own projects in it in the past year. I did this tutorial in March. I was attracted to it because it required a very minimal amount of "stuff". It uses shadow-cljs with minimal deps, you can use any editor you like, and it focuses on the ideas and concepts with simple examples. Loved it.
@tilman.schieber Here's what I just tried (and it worked for me):
> lein new luminus tilman +reagent
...
> cd tilman
> lein run
... starts HTTP server and nREPL server ...
then in another terminal in the same project folder > lein figwheel
...
waits for you to visit http://127.0.0.1:3000 in your web browser Prompt will show when Figwheel connects to your application
[Rebel readline] Type :repl/help for online help info
ClojureScript 1.10.520
app:cljs.user=>
and you can connect an nREPL client (e.g., your editor) to 127.0.0.1:7000@tilman.schieber about deprecation, yeah cljs has moved pretty fast
Your browser should refresh and show https://www.dropbox.com/s/dl8r93awgywn7r8/Screenshot%202019-08-29%2017.50.04.png?dl=0
@tilman.schieber it's also worth getting the Web Development in Clojure book by Sotnikov, it uses luminus iirc
ClojureScript has evolved very, very fast and a long way over the last few years. We tried it at work back in 2014/2015 I think and it was... not a good experience... but I've recently come back to trying it again, and I'm following along with Dimitri's book in the new edition (which is in beta right now).
Gain expertise in the popular Ring/Compojure stack using the Luminus framework.
I remembered correctlyYeah, that one... it's good.
The new edition has been updated to "current" cljs techniques but this whole area moves so fast 🙂
@seancorfield thank you! I'll try that now
Chapter 4 introduces cljs and Reagent and chapter 5 introduces shadow-cljs (a change from Figwheel in the previous edition).
Also, did you run through this -- the official ClojureScript Getting Started Guide? https://clojurescript.org/guides/quick-start
(and I suppose I should ask: what platform are you on? Windows is kind of a second-class citizen in the Clojure world)
Hmm, I would expect the official guide to "just work" on Linux 😐
But I think open jdk12 might have been a problem. After downgrading to an older Java it worked at least to the point of showing a repl
oh yeah, 12 would be ouchy
Ah, yes, JDK9+ definitely threw a bit of a wrench in things but JDK11 should mostly be OK these days with Clojure/Script.
since 12 isn't ltr I don't think anyone has really tried to make clojure work with 12
I’ve used both 12 and 13, not aware of any issues
I'm still using JDK8 (Zulu OpenJDK with JavaFX bundle locally, AdoptJDK OpenJDK elsewhere)...
the tl;dr about the newer vms is that part of the new java changes is being more strict about some kinds of shenanigans that allow access to other modules, and clojure (like most languages that try to do dynamic compilation and target jvm bytecode) used some of those shenanigans
Thanks for the quick help. I should've asked you three hours ago. Luminus with leiningen (what @seancorfield recommended) seems to work just fine
That's what we're here for! 🙂
I expect to go back to Dmitri's book and renew my cljs learning in September. My wife is away for two weekends (Shanghai, China and Milwaukee, WI, judging cat shows) so I'll be all alone with nothing to do but read books and play in the REPL 🙂
Technologies that are developing so fast are so hard to find good information on without someone pointing you in the right direction. I'll definitely think about buying the book. I love pragmatic programmers
Yeah, I have 19 of the PragProg books in my DropBox account 🙂
Mostly Clojure books...
Well, maybe not mostly but quite a few 🙂
So I have 4 wrong answers now thanks to (take 4 (shuffle wa2))
but I also have the right answer on hand, and would like to throw it into this mix.
ah i got it, just did a concat and another (shuffle) 😃
ive liked the book "living clojure"
Hi all. New to Clojure and learning by jumping into the deep end on a freelance job. I'm trying to find some documentation on this syntax: {config as ::conf/config}
where conf is required with (:require [myapp.config :as conf])
.
that's invalid, hash-maps don't accept odd numbers of keys, even in special syntaxes
::conf/config is just a shorthand that expands to :myap.config/config
:as is used to bind values in destructuring, but you can't bind to a keyword
I should have added more context: (defroutes routes (GET "/foo/bar" [:as {config ::conf/config}] ...
OK, defroutes is weird, but at least that's valid for the clojure reader :D - note that :as is outside the map
it's saying that ::conf/config of the incoming request is bound to the symbol config in the ...
code
it's part of compojure
compojure routes run inside an implicit destructure, https://clojure.org/guides/destructuring - this guide might help
Thanks @noisesmith!
anyone using Netlify to deploy clojure? I’m not sure what build command I should use. I’m using lein
I'm curious why my attempt at the simple robot name
problem on exercism doesn't work right. https://pastebin.com/RKqTsujQ
When I run each individual function in the repl they all work as I think they should but when I evaluate the let
expression at the bottom (taken from one of the tests), it returns the same name for all 3 bindings.
notice how a-robot
is used for every call in the let bindings. When (robot-name/reset-name a-robot)
is called, it's not returning a new name. it's changing the value of a-robot
in place.
and I guess specifically notice that a-robot
is not being rebound in the let.
So how exactly is the test's its-new-name
supposed to return a new name with how they have that set up then?
here is the full test:
(deftest reset-name
(let [a-robot (robot-name/robot)
its-original-name (robot-name/robot-name a-robot)
its-new-name (do (robot-name/reset-name a-robot)
(robot-name/robot-name a-robot))]
(testing "reset-name"
(is (re-seq #"[A-Z]{2}\d{3}" its-new-name)
"new name matches expected pattern")
(is (not= its-original-name its-new-name)
"new name is different from old name")
(is (= its-new-name (robot-name/robot-name a-robot))
"new name doesn't change until you reset it")
(is (not= its-new-name (do (robot-name/reset-name a-robot)
(robot-name/robot-name a-robot)))
"new names are different each time"))))
it works because the functions are wrapped in a do
the first call in the do
does the side-effectful change to a-robot
and the second call gets the current name from a-robot
a do
will evaluate multiple forms in order and will return the value of the last form.
ok. so I think I need to make sure reset-name
actually changes a-robot
and doesn't just generate a new random robot name, huh?
yeah, a-robot
can't just be a String name, it needs to be some kind of holder of a name that can change.
that sounds like mutation! or can it just be changed locally like in a let binding. let me try and wrap my head around this and play around some more.
no problem 🙂
Hi all. What is the difference in spec between using (s/with-gen ...)
and (s/spec ... :gen ...)
?
I'm seeing each used in different examples online, but they seem to be doing the same thing...
kind of just accidental artifact of different api explorations
s/coll-of has a :gen key too that's another overlap
I suspect we will winnow some of those down in spec 2
Ah ok. Thanks for explaining - I thought that might have been the case, but wanted to check I wasn't missing something.
Is it possible to pass data into a spec generator? I'm trying to "compose" generators so that I can generate a sub-map according to data already generated at the top level
The idea would be generate a tuple, then pass one of those tuples into another generator to generate a random sample (e.g. a map of that particular size)
I've not explained that very well, so I'll try and work up a simplified example of what I'm trying to achieve...
@j.m.frith is gen/fmap
what you are looking for?
As a simplified example, I am trying to create a map like
{:deck/cards {:trumpet {:value 1}
:snake {:value 8}
:exhaust {:value 0}}
:game/card-1 :trumpet
:game/card-2 :snake}
so I was intending to generate a random set of keywords which I can pass into my generator which can create a deck from the cards (by generating random :value), but then also use the same set of keywords to choose a random element for the two :card valuesThat way I can generate a complete game map. But if a function only needs the :deck/cards as input, I'd like to use the generator to generate a random set of keywords and then stick in :values
Actually just asking the question tells me, maybe in the first instance I should just generate a completely random deck, then extract the keywords that were generated in that to choose what should be in the :game/card-1 and :game/card-2 values
the advantage of using fmap for this is it helps to preserve the reproducability of generation based on a seed
(in case that isn't clear)
Hi @seancorfield I think it's part of what I'm looking for, but really I'd want to use it to pass parameters into a second generator (i'm just not sure that part is possible/sensible)
fmap takes a generator and a function, generators don't take args, but your function can generate another value and combine it with what the original generator made
Thanks @noisesmith I think I'm getting close to understanding, I guess what I'd ideally like is rather than the function combining it with what the original generator made, it could modify the original generator to produce only a subset of what it otherwise could do.
I'm wondering if I should write a separate function which would return a generator with the properties I want
you can literally filter with gen/such-that
Yeah, but such-that needs it to be relatively likely that things will satisfy the criteria
It's a bit like the general spec calls for a vector with length between 1 and 10, but if data elsewhere in my spec has a value of 9, then the list has to be of length 9
right, so you can use s/with-gen and gen/fmap to make a a generator that that is more likely to create the correct values
I was originally thinking that I should generate the 9, then I can "pass that into a generator" to generate a vector with 9 entries.
hard-coding length 9 is a perfect usage for fmap
sure, higher order function can take 9 and return an fmapping function, if that's the semantics you want
Would I be better off generating a random length vector, and then putting whatever length it is into the other part of the spec?
specs don't really do that self-referential thing, but custom generators can
I think that's what I'm struggling to get my head around. The spec at a higher level can enfore the relationship, but ideally I'd like to be able to have generators that will work at whatever level they are
I'd prefer providing that info explicitly, over duplicating info (AKA making the spec brittle by having two values that can go out of sync and produce something incoherent)
IMHO a spec should already be de normalized (fixed typo)
OK. Thank you. I will go and think about how I can achieve this. It's been really helpful!
Why can't (source
get my function definition?
user=> (defn sq [x] (* x x))
#'user/sq
user=> (sq 2)
4
user=> (clojure.repl/source sq)
Source not found
nil
source doesn't get data from the function, it uses file / line metadata
you can't find a file for stdin
oic -- so, there's no function that can recall the definition of something that I make in the REPL?
it's never stored, right
the repl/source function uses metadata to reread the souruce
compare
(ins)user=> (defn foo "this is foo" [])
#'user/foo
(ins)user=> (pprint (meta #'foo))
{:arglists ([]),
:doc "this is foo",
:line 1,
:column 1,
:file "NO_SOURCE_PATH",
:name foo,
:ns #object[clojure.lang.Namespace 0x6a969fb8 "user"]}
nil
(ins)user=> (pprint (meta #'conj))
{:added "1.0",
:ns #object[clojure.lang.Namespace 0x57b9e423 "clojure.core"],
:name conj,
:file "clojure/core.clj",
:static true,
:column 1,
:line 75,
:arglists ([coll x] [coll x & xs]),
:doc
"conj[oin]. Returns a new collection with the xs\n 'added'. (conj nil item) returns (item). The 'addition' may\n happen at different 'places' depending on the concrete type."}
nil
the metadata of conj contains :file
and :line
and :column
such that clojure.repl/source can find the source and print it
I've not used (meta
before:
user=> (clojure.pprint/pprint (meta #'sq))
{:arglists ([x]),
:line 1,
:column 1,
:file "NO_SOURCE_PATH",
:name sq,
:ns #object[clojure.lang.Namespace 0x3d7b1f1c "user"]}
nil
so ... if I want to read the source of a function I made in the repl as data for another function .. what's the function I should use for that?
or should I quote it and use it that way?
sadly clojure source code isn't first class like that (though there are libs with their own version of fn / defn that provide this)
Oh? Could you link to one or more of those libs?
You're right! This is functionality I wanted in my rebel-readline
@mfikes: is this capability unique to cljs REPLs?
No, it is just a revision to the way those REPLs behave. If you are curious, it just captures the source for REPL-entered forms. https://github.com/planck-repl/planck/commit/f16e6187f09a0201b3b2cefbfd8e0c1d75fa053d
I guess (::repl-entered-source var)
is the magic
@johnjelinek My advice: develop a workflow where you never type into the REPL -- always put the code in a file, connect your editor to the REPL, and eval forms from the file into the REPL.
Stu Halloway and many others advocate for that -- never type into the REPL. You can put (comment ,,,)
forms in your (source/test) files for code you are just experimenting with and do not want run as part of your project, then you can eval the forms from inside that comment
block as and when you need.
I was really hoping to do more with rebel-readline
instead of using my editor, but that's ok
The REPL is fine for really basic exploration but it's all throwaway work (well, the REPL history is actually saved to a local file -- and you can scroll back in the REPL in all except the very basic clojure.main
REPL). Editor integration is definitely the way to go.
It's why I like REBL so much: it does have an input REPL (well, it has two) and it shows all expression history -- even from your editor -- and lets you scroll back and forth and drill into values and display them in different ways etc...
...but I drive it almost entirely from my editor (in terms of evaluation).
@seancorfield: is there any good written resources you recommend to incorporate REBL into my workflow?
Written? No, not really. Much will depend on what editor you use and what "build" tool you use.
I've done a few screen casts of my workflow with Atom/Chlorine/REBL up on YouTube
(but it all assumes CLI/`deps.edn`, my dot-clojure repo, and my atom-chlorine-setup repo 🙂 )
For nREPL-based workflows: https://github.com/RickMoynihan/nrebl.middleware
In case anyone is curious at this point, if you have the rebl-8
and nrepl
aliases in deps.edn
based on my dot-clojure repo, you can get nREPL + REBL up and running with Rick's middleware like this
clj -R:rebl-8:nrepl -Sdeps '{:deps {rickmoynihan/nrebl.middleware {:mvn/version "RELEASE"}}}' -e "((requiring-resolve 'cognitect.rebl/ui))" -m nrepl.cmdline --middleware '[nrebl.middleware/wrap-nrebl]' -i
(assuming you're using Clojure 1.10 or later because... why wouldn't you?)
That command starts the REBL UI, starts an nREPL server, and starts an interactive REPL, all connected. You can then connect your editor/whatever to nREPL on the port displayed as it starts up, e.g.,
nREPL server started on port 58945 on host localhost -
nREPL 0.7.0-alpha1
Clojure 1.10.1
OpenJDK 64-Bit Server VM 1.8.0_222-b10
Interrupt: Control+C
Exit: Control+D or (exit) or (quit)
user=>
You may want the CIDER middleware in there too (if you're using Emacs) -- see Rick's project README
cool, thanks!
next topic: by convention, should any function that involves side-effects be appended with a !
to the function name?
(e.g: write to file / save to database / swap atom)
It depends 🙂
The idea behind !
is to identity functions that you should not call as part of an STM transaction (as I recall) but that would mean that !
would filter all the way to the top of your call stack on nearly everything...
...so, in reality, it's normally used just for low-level functions that are known to modify "the environment".
next.jdbc/execute!
and next.jdbc.sql/update!
for example, but I don't typically add !
to functions that call those (unless they are thin wrappers that a lot of other code would call instead).
See https://github.com/bbatsov/clojure-style-guide#changing-state-fns-with-exclamation-mark
(also https://github.com/bbatsov/clojure-style-guide#refs-io-macro but almost no one uses ref
s to be honest)
cool, thanks!
really? no one uses refs
? not even for transactions where you're going to be producing side-effects in different places?
I wouldn't say that no one uses Clojure ref
s. They are seldom used, because atom
tends to be enough for most purposes.
e.g. if you have a big map of keys and values, perhaps nested, storing a bunch of configuration data, a single atom
is enough to make atomic changes to arbitrary parts of that, all at once.
This 👆:skin-tone-2: was one of the bigger surprises to me when I first got started with Clojure.
I think a lot of people do, when they first see them.
A lot of people see STM as "unique to Clojure" and so they expect it to be used a lot. But mostly Clojure code avoids side-effects so much that you don't need STM much -- beyond a few atom
and occasional agent
calls.
Yes. I've been using Clojure in my day job since around 2013 and I don't think I've ever reached for refs / dosync
.
so ... let's say I'm primarily going to be writing cloud native stateless processes (maybe they run in containers or lambdas), so I'll defer concurrency concerns to the infrastructure of the cloud -- do I lose a lot of the power of clojure by not leveraging its concurrency and parallelism capabilities?
like ... would you say clojure is the wrong tool for the job in that context?
I would definitely not say that. You'll still get a lot of value out of the rest of Clojure.
I have heard other Clojure developers say that if they had to choose between a language that had the syntax of some Java/Python/Ruby/etc. language, but Clojure's immutable data structures, vs. Clojure syntax but mutable data structures, they would go for the immutable data structures every time.
Even for single-threaded programs.
large mutable data structures, even in a single-threaded program, are a source of incidental complexity, bugs, difficulty-in-people-reasoning-about-code-behavior, etc.
In many ways immutable data structures are the best thing about Clojure to me. It's why I'm a bit OCaml-curious.
hey everyone! im trying to validate clojure as a viable option for an API server but am curious about a few things 1: Is there a defacto standard for http/restful endpoints? I have come across - Ring - Luminus 2: Is there a lib for JWT? I have come across - Buddy But it hasn’t been updated in two years? can anyone provide me any guidance here on good resources i could read up about auth / endpoint creation
@tjb: I think you may want to take a look at pedestal
and then I think buddy should be fine
Activity is not necessarily a sign of deprecation in Clojure-land. Since Clojure itself is extremely stable a lot of libraries simply get to a state where they're more or less complete and modifications aren't necessary.
I've used buddy
in production at my last job and my current one. Sometimes no updates means no problems.
some background: i have a node server for my side project but really want to expand my FP knowledge. my day job is java stuff so staying on the jvm seems like the right move. in comes clojure. since im primarily a web dev im want to validate using clojure for api server-related things before mentioning it @ work as a viable option for things
Ring is good for learning
I think most other librairies start with ring as basis
more bare-bone the better. or anything similar to what express (node) is would be awesome too
Re: "de-facto standard" — You'd probably be interested in this quote from Rich on web frameworks: >>> JOY CLARK: … So is there a benefit to having a standard stack, where you can say "This is what you should use?" RICH HICKEY: Well, some parts of that question are social, which I can't really speak to... I think certainly when somebody figures out how to do web development, they should encode it in a framework. But I'm not sure that that's a solved problem, and I think until it is a solved problem, opinions are very much opinions, and therefore you're at risk adopting a set of opinions that may not be an answer. — http://www.dustingetz.com/:rich-hickey-web-frameworks/
Yes, that would definitely be Ring. It's a pretty minimal HTTP request/response abstraction.
Yeah, I still like all the middleware xD
I personally found that implementing some Ring middleware by hand helped me understand each of the Ring-based libraries better, and how to choose between them.
I love lein ring for developing the API
Yeah, it makes a lot of sense to develop some middleware
awesome thanks everyone i really appreciate it. im going to dive into it tomorrow and experiment. i will post my learnings tomorrow night!
I believe that Pedestal is analogous to Ring, but my impression has been that it gets less use overall (even though it's produced by Cognitect).
@tjb We have a production API Server built with Ring, Compojure (for routes), and some standard Ring middleware for handling JSON in and JSON out. Plus seancorfield/next.jdbc
for handling JDBC communication with databases (and a bunch of other libs for external stuff).
@tjb If you have the Clojure CLI installed, you can create a basic Compojure project like this
clj -Sdeps '{:deps {seancorfield/clj-new {:mvn/version "0.7.8"}}}' -m clj-new.create compojure tjb/api-example
and that will give you a typical structure for a Clojure project.If you have Leiningen installed, lein new compojure tjb/api-example
should do the same
As you write more code, you'll have more namespaces under src
and (hopefully) the matching namespaces under test
with unit tests in.
The compojure
template assumes you'll use lein
to do things with the project, but it would be easy enough to add deps.edn
with the equivalent dependencies
(and unfortunately it assumes you'll use lein ring ...
to start the project which I really don't like but you should be able to find tutorials to get up and running quickly that way)
If you want to see a more fleshed out web app example, just to get you started https://github.com/seancorfield/usermanager-example (not an API but it works with clj
/ deps.edn
from the get-go and shows how to do DB access etc)
Awesome thank you so much @seancorfield !!!
what is reduce1
and why can't I (source
it?
user=> (clojure.repl/source +)
(defn +
"Returns the sum of nums. (+) returns 0. Does not auto-promote
longs, will throw on overflow. See also: +'"
{:inline (nary-inline 'add 'unchecked_add)
:inline-arities >1?
:added "1.2"}
([] 0)
([x] (cast Number x))
([x y] (. clojure.lang.Numbers (add x y)))
([x y & more]
(reduce1 + (+ x y) more)))
nil
bootstrapping crutch
We use JWT and I think we just wrapped the Apache lib for it... let me check...
you should just read reduce1 as reduce
does (reduce1
basically do a (loop [] (recur
?
(:import (org.apache.oltu.jose.jws
JWS$Builder
signature.impl.SymmetricKeyImpl
signature.impl.SignatureMethodsHMAC256Impl)))
Yeah, org.apache.oltu.jose/org.apache.oltu.jose.jws
@johnjelinek https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L930
(`clojure.core` == "Here Be Dragons!" 🙂 )
bootstrapping is weird sometimes :)
IIRC, reduce1
has the same API as reduce
, but reduce1
might not have all of the performance optimization goodness that reduce
has.
I'm a little surprised I don't have to (first
and (next
here with the more
:
user=> (defn sq
#_=> ([x] x)
#_=> ([x y] (reduce * (repeat y x)))
#_=> ([x y & more] (reduce sq (sq x y) more)))
@johnjelinek Because [x y & more]
destructures the arguments for you... so (sq 1 2 3 4)
becomes x : 1, y : 2, more : (3 4)
is there a way to import a function from another file with a different name (i'm trying to overwrite fdef with >fdef from ghostwheel)
(:require [ghostwheel :refer [fdef :as >fdef]])
is this a thing?
@dfehrenbach04: this works for me: (require '[clojure.repl :refer [doc] :rename {doc docs}])
There's :rename
which takes a hash map
so, you could prolly (:require [ghoswheel :refer [fdef] :rename {fdef >fdef}])
Yeah that worked. Thanks a million!
Ghostweel's being... annoying when it comes to playing nicely with Calva in vscode. That's the next problem to fix on my list.
(Tons of "problems" including being unable to "resolve symbols" when using the >defn
from ghostwheel 😞 )
some next level stuff right here: https://github.com/clojure/math.numeric-tower/blob/08d50ecec92c9aad8d13041013d5c0f2a28bb895/src/main/clojure/clojure/math/numeric_tower.clj#L72-L78
@johnjelinek In what way? (I guess if you have a math background it's straightforward)
ya, that's prolly my problem -- I don't have a math background and most of my experience has been trying stuff out and seeing what happens -- which is what drew me to clojure, because I could get a similar fast feedback loop like I'd get in SQL compared to other languages where my feedback comes back slower (unless I partition a segment in unit tests)
but as you can see -- what I wrote above to for (sq
compared to (exp-int
... and you can prolly see how they're at different levels
It's a fast way to calculate exponents: you square the value and half the exponent and adjust by tracking the "remainder" multiplication at each time round the loop.
If you compared your sq
and that expt-int
side-by-side written in C or Java they'd still look pretty different
I think a fair number of comp sci students find that novel when they first see it. I know I did not come across it until grad school, and wondered how I had never thought of it before.
Even more cool when I first saw it: using multiplication of 2x2 matrices of the appropriate contents in order to calculate the N-th Fibonacci number in log N matrix multiplications.
or maybe they are 3x3 -- long time since I looked at it: https://www.geeksforgeeks.org/matrix-exponentiation/