C# Understanding C# Method for Calculating Factorials

  • Thread starter Thread starter Chestermiller
  • Start date Start date
  • Tags Tags
    Method
AI Thread Summary
C# methods can indeed call themselves, a concept known as recursion, which is used effectively in calculating factorials. The provided factorial method demonstrates this by multiplying the input value by the factorial of the input value minus one until it reaches the base case. It's essential to handle the case for 0! = 1 to prevent overflow and infinite recursion. While recursive functions can be elegant, they may be less efficient than iterative solutions like for loops. Understanding the logical flow of recursive calls is crucial for mastering this programming technique.
  • #51
Paul Colby said:
Cool, so the ADF435 frequency synthesizer chip has 6 hardware registers that are set by serial toggling various GPIO lines in the proper sequence. How is this done in Haskell?
What does that have to with the term 'side effects' as used in functional languages? Haskell can operate entirely inside a VM. Mediation of peripheral devices is up to the VM control program.
 
Technology news on Phys.org
  • #52
sysprog said:
What does that have to with the term 'side effects' as used in functional languages?
That's the bit I don't understand. Register values are not local as far as I can tell. Hardware has a state which needs to changed. Printing is a similar problem. Clearly language constructs exist to print, and one needed a special IO thingy to do it. How does this make Haskell less annoying?
 
  • #53
Paul Colby said:
That's the bit I don't understand. Register values are not local as far as I can tell. Hardware has a state which needs to changed. Printing is a similar problem. Clearly language constructs exist to print, and one needed a special IO thingy to do it. How does this make Haskell less annoying?
Obviously register values are not available directly at Haskell's level of abstraction. The purveyors of Haskell don't tout it as well-adapted for low-level hardware interfacing. Apparently your purposes are better served by imperative languages.
 
  • #54
Think the idiomatic way to write a factorial function in Scheme would look more like this:
Code:
(define (factorial n)
  (let loop ((k n) (acc 1))
    (if (<= k 1)
        acc
        (loop (- k 1) (* k acc)))))
This version is tail recursive so it won't blow the stack if n is large. It uses a feature called "named let" to define a local helper function (called "loop" here) with parameters k and acc and immediately call it with k = n and acc = 1.

(Common) Lisp doesn't have tail call elimination as part of the language. Many implementations support it but (unlike Scheme) it isn't required by the language standard and some implementations don't or only partially support it. So you can't rely on tail recursion in portable Lisp code. But this isn't a problem because Lisp has perfectly good loops:
Code:
(defun factorial (n)
  "Compute the factorial of non-negative integer N."
  (check-type n (integer 0 *))
  (loop for result = 1 then (* result k)
        for k from 2 upto n
        finally (return result)))
 
  • Like
Likes sysprog
  • #55
sysprog said:
Obviously register values are not available directly at Haskell's level of abstraction. The purveyors of Haskell don't tout it as well-adapted for low-level hardware interfacing. Apparently your purposes are better served by imperative languages.

Okay, so I've said as much, right? Correct me if I'm in error. Machine state is a "side effect" in Haskell's level of abstraction, right? Machine state is very much the equivalent of setting a global variable in a program from a pure function. The language designers had to add a special IO thingy to the language to even operate on a printer.

Again, I also concede that important programming constructs, design patterns and concepts are present in Haskell. As a toy the language is fine IMO.
 
  • #56
wle said:
This version is tail recursive so it won't blow the stack if n is large.
According to R5RS tail recursion is done if the last expression evaluated is a call to the function being recursed. AFAIK that's true for the version I gave as well.

(if condition <tail expression> <tail expression>)
 
  • #57
stevendaryl said:
I don't actually know how recursion is implemented, at the machine level.

As far as the function call itself, it's just an ordinary function call, which at the machine level is just a jump instruction to the top of the function. The key is that the function itself has to be reentrant--i.e., you have to be able to have multiple calls to it active at the same time without one call clobbering another. That means that, ideally, function parameters and local variables should be stored on the stack, not in registers. For languages which insist on having those things in registers, every recursive call to the function needs to save registers on the stack before making the call, and then restore the registers from the stack after the call.
 
  • #58
It's in the nature of computer languages that the more they are designed for a specific use, the worse they tend to become for other uses. If Haskell is not a good language in general for device handlers, it joins the ranks of Cobol, SQL, OpenGL, MATLAB, SLAM, SimScript, etc., which are good languages for their intended purpose.
 
  • #59
Paul Colby said:
According to R5RS tail recursion is done if the last expression evaluated is a call to the function being recursed. AFAIK that's true for the version I gave as well.

The last expression evaluated in your version is (* N (factorial (- N 1))), which is a multiplication. Your function has to evaluate the sub-expression (factorial (- N 1)) before it can evaluate the product with N.
 
  • Like
Likes Paul Colby
  • #60
Paul Colby said:
Machine state is a "side effect" in Haskell's level of abstraction, right?
The short answer to that question is yes, but in this context it's in my view kind of a loaded question. Haskell uses monadic constructs for such things as IO, clock, and emulation of
(Haskell-specific) finite state machines The real machine state in a computer running Haskell is not available unmediatedly to a program written in the Haskell language.

Here's a page which may provide a charitably good-humored perspective on Haskell:
The Evolution of a Haskell Programmer

[I recommend using the following bookmarklet (copy it and paste it into the address bar to fix the page colors; save it as a bookmark to use it on other similarly annoying pages) to fix the obnoxious color scheme on that page]
Code:
javascript:(function(){var newSS, styles='* { background: white ! important; color: black !important } :link, :link * { color: #0000EE !important } :visited, :visited * { color: #551A8B !important }'; if(document.createStyleSheet) { document.createStyleSheet("javascript:'"+styles+"'"); } else { newSS=document.createElement('link'); newSS.rel='stylesheet'; newSS.href='data:text/css,'+escape(styles); document.getElementsByTagName("head")[0].appendChild(newSS); } })();
 
Last edited:
  • #61
So, monadic constructs are things I really didn't get when I was looking at Haskell. The "types" of problems I was playing with at the time were computer algebra type things, vector spaces, groups and such. Just trying to learn. I came away feeling okay, cool, what do I get from this effort?
 
  • Like
Likes sysprog
  • #62
Paul Colby said:
So, monadic constructs are things I really didn't get when I was looking at Haskell. The "types" of problems I was playing with at the time were computer algebra type things, vector spaces, groups and such. Just trying to learn. I came away feeling okay, cool, what do I get from this effort?
I would just use MATLAB (or Mathematica/Wolfram) for most of that. For one-offs, my TI-83 is usually more than adequate for my purposes. If I need to do, e.g., a computationally intensive set of Fourier transforms, I'll use routines out of the Fortran library.

Most of my programming work is in IBM mainframe OS interfacing, and most of that is done in assembly language. Some of it is fixing other people's code, in whichever language they wrote it in.

On the PC, I prefer to use assembly language over using high-level languages, but I'll work with whatever best gets the job done. Subject to the condition that I prefer to code in languages I already know, in general, if I have a choice, I try to use whichever language I think is best for the problem I'm dealing with.
 
Last edited:
  • #63
I just reinstalled Haskell, I'm holding you directly responsible.
 
  • Haha
Likes sysprog
  • #64
The plus side of using functional languages (real functional languages, without side-effects, not languages that just look functional) is referential transparency. If you convince yourself that an expression always evaluates to some particular value, you can replace it by that value, and your program will work the same way. That's a powerful way to reason about programs.
 
  • Like
Likes Nugatory and sysprog
  • #65
For @Chestermiller or anybody else, there's a nice book by RB Whittaker I forgot its name on C# so far looks like a great book, and also a website with solutions to the problems in it.
 

Similar threads

Back
Top