ackermann
Full source code
C++
// -*- mode: c++ -*-
// $Id$
// http://www.bagley.org/~doug/shootout/
#include <iostream>
#include <stdlib.h>
using namespace std;
int Ack(int M, int N) { return(M ? (Ack(M-1,N ? Ack(M,(N-1)) : 1)) : N+1); }
int main(int argc, char *argv[]) {
#ifdef SMALL_PROBLEM_SIZE
#define LENGTH 11
#else
#define LENGTH 12
#endif
int n = ((argc == 2) ? atoi(argv[1]) : LENGTH);
cout << "Ack(3," << n << "): " << Ack(3, n) << endl;
return(0);
}
Rust
// Adapted from https://github.com/llvm/llvm-test-suite and // http://www.bagley.org/~doug/shootout/ use std::env; #[cfg(feature = "small_problem_size")] const LENGTH: i32 = 11; #[cfg(not(feature = "small_problem_size"))] const LENGTH: i32 = 12; fn ack(m: i32, n: i32) -> i32 { if m != 0 { ack(m - 1, if n != 0 { ack(m, n - 1) } else { 1 }) } else { n + 1 } } fn main() { let mut args = env::args(); let n = if args.len() == 2 { args.nth(1).unwrap().parse::<i32>().unwrap() } else { LENGTH }; println!("Ack(3,{}): {}", n, ack(3, n)); }
Porting notes
Conditionally declaring constants
C++
#ifdef SMALL_PROBLEM_SIZE
#define LENGTH 11
#else
#define LENGTH 12
#endif
Rust
#![allow(unused)] fn main() { #[cfg(feature = "small_problem_size")] const LENGTH: i32 = 11; #[cfg(not(feature = "small_problem_size"))] const LENGTH: i32 = 12; }
It's idiomatic to use const
variables along with the cfg
attributes to conditionally declare constants, as opposed to using macros in C/C++.
Declaring a function
C++
int Ack(int M, int N) { return(M ? (Ack(M-1,N ? Ack(M,(N-1)) : 1)) : N+1); }
Rust
#![allow(unused)] fn main() { fn ack(m: i32, n: i32) -> i32 { if m != 0 { ack(m - 1, if n != 0 { ack(m, n - 1) } else { 1 }) } else { n + 1 } } }
Declaring a function in Rust is not too dissimilar to C++ with some differences:
- Functions are declared with the
fn
keyword. - The parameter type comes after the parameter name and a colon.
- The return type comes after the parameter list and the
->
. - The if statement condition expression doesn't need parentheses around them.
- Rust has if expressions in addition to if statements (or the last statement in the blocks can be an expression), and lacks the ternary operator.
- Rust has no
int
type and uses integer types with explicit widths such asi32
.
Accessing program arguments
C++
int main(int argc, char *argv[]) {
int n = ((argc == 2) ? atoi(argv[1]) : LENGTH);
...
Rust
fn main() { let mut args = env::args(); let n = if args.len() == 2 { args.nth(1).unwrap().parse::<i32>().unwrap() } else { LENGTH }; ...
Rust's main function doesn't have the argc
and argv
parameters. Instead the program arguments are accessed with the std::env
API.
The std::option
type is commonly used in Rust to represent the optional result of an operation. The unwrap
function is used to access the result when it is assumed that it contains a valid result, which would cause a panic if it does not. For example, accessing the n
th element of an argument array and parsing a string into an integer.
This common pattern on the program arguments are used in most of the programs here. I believe it achieves dual purposes, to allow the problem size changed and to makes it harder for the compiler to compile-time evaluate (constant-fold) the entire program logic.
Printing to standard output
C++
cout << "Ack(3," << n << "): " << Ack(3, n) << endl;
Rust
#![allow(unused)] fn main() { println!("Ack(3,{}): {}", n, ack(3, n)); }
println!
is the standard way to print to standard output in Rust. The !
at the end indicates it's a macro. I won't go into macros here.
To print without the new line, we'd use print!
instead.