I just started learning rust and I'm creatively trying somethings as I read "the Rust Book".
I know it is possible to create a generic method to get the largest element from an array, like the following:
fn largest<T: PartialOrd + Copy> (nums: &[T]) -> T {
let mut largest = nums[0];
for &el in nums {
if el > largest {
largest = el;
}
}
return largest;
}
And calling the main function like this:
fn main() {
let list: Vec<u32> = vec![1,7,4];
println!("{}", largest(&list)); // 7
}
How would I go doing the same thing but "extending" the array, like this:
fn main() {
let list: Vec<u32> = vec![1,7,4];
println!("{}", list.largest()); // 7
}
I guess the final question is: if it is possible, would it be a bad practice? Why?
I tried something like this, but didn't manage to figure out how to implement the "Largeble" trait returning the T:
pub trait Largeble {
fn largest(&self);
}
impl<T: Copy + PartialOrd + Display> Largeble for Vec<T> {
fn largest(&self) {
let mut largest = match self.get(0) {
Some(&el) => el,
None => panic!("Non Empty array expected")
};
for &el in self {
if el > largest {
largest = el;
}
}
println!("{}", largest);
// return largest;
}
}
You need to make the Largeable trait return a T from the Vec<T>, which you can do with an associated type:
use std::fmt;
pub trait Largeble {
type Output;
fn largest(&self) -> Self::Output;
}
impl<T: Copy + PartialOrd + fmt::Display> Largeble for Vec<T> {
type Output = T;
fn largest(&self) -> T {
let mut largest = match self.get(0) {
Some(&el) => el,
None => panic!("Non Empty array expected")
};
for &el in self {
if el > largest {
largest = el;
}
}
largest
}
}
println!("{}", vec![1, 2, 3, 2].largest()); // prints "3"
Traits like Largeable are usually called extension traits, since they extend existing items with new features. Using extension traits to extend items in existing libraries is common in the Rust ecosystem. It's common to suffix the names of extensions with Ext (so a collection of additional methods for Vec would be called VecExt). A popular use of extension traits is the itertools library, which provides a trait that adds additional useful methods to Iterator in the standard library.
How would I go doing the same thing but "extending" the array
Sure, your code snippet was close, you can create a trait and implement it on types.
pub trait Largeble<T>
where
T: Ord,
{
fn largest(&self) -> Option<&T>;
}
impl<T> Largeble<T> for Vec<T>
where
T: Ord,
{
fn largest(&self) -> Option<&T> {
// Iterator already has a method for getting max which simplifies things
self.iter().max()
}
}
Alternatively, you can make T an associated type which may be better suited to this example.
You can run this code in the Rust Playground.
Would it be a bad practice? Why?
Nope, it's definitely not bad practise. It is a very common way to developing in Rust and can be very powerful. Your trait needs to be in scope for you to be able to call .largest(), so it does not pollute anything.
Additionally, if you have multiple methods with the same name from different traits, you can provide a longer syntax to specify the exact trait you want to use: Largest::<u32>::largest(&list).
I tried something like this, but didn't manage to figure out how to implement the "Largeble" trait returning the T.
Your code was mostly correct, but your largest method didn't return anything. That's why the trait needs a generic T, to specify that you will return T.
Let's say I want to iterate over an array in a way that isn't well-supported by the Js/Belt standard library functions. For example, perhaps I need to examine pairs of elements at a time. With a list, this is straightforward to do in a recursive style:
let rec findDouble = (list) => switch list {
| list{a, b, ..._} when a == b => a
| list{_, b, ...rest} => findDouble(list{b, ...rest})
| _ => 0
}
list{7, 9, 10, 10, 11, 13} |> findDouble |> Js.log // 10
However, ReScript seems to gently discourage lists in favor of arrays (see: the clumsier list syntax and the absence of list equivalents of certain standard library functions like Belt.Map.fromArray), so I'm not sure if converting an array to a list just to use this style is idiomatic - especially if the function produces a list that must then turn back into an array.
Of course I can use mutability to implement the function in a traditional imperative way:
let findDouble = (arr) => {
let idx = ref(1)
let answer = ref(0)
while (idx.contents < Js.Array.length(arr)) && (answer.contents == 0) {
if arr[idx.contents] == arr[idx.contents - 1] {
answer := arr[idx.contents]
}
idx := idx.contents + 1
}
answer.contents
}
[7, 9, 10, 10, 11, 13] |> findDouble |> Js.log // 10
But this is ugly and runs counter to the functional bones of ReScript.
What is a clean, idiomatic way to implement this function?
You can still use recursion, just with incrementing an index instead of using the tail of the list:
let findDouble = arr => {
let rec loop = idx =>
if idx >= Array.length(arr) {
0
} else if arr[idx] == arr[idx - 1] {
arr[idx]
} else {
loop(idx + 1)
}
loop(1)
}
I am following the excism rust track, and I've hit a problem (I'm very, very new to rust)
This is a function to calculate the pythagorean triples of an integer:
use std::collections::HashSet;
use rayon::prelude::*;
pub fn find(sum: u32) -> HashSet<[u32; 3]> {
let a_b_plus_c: Vec<(u32; 2)> = (1_u32..(sum / 3_u32)).into_par_iter()
.filter_map(|a| {
let b_plus_c: u32 = sum - a;
let whole_number_check: Option<u32> = (b_plus_c.pow(2) - a.pow(2)).checked_rem(b_plus_c * 2);
match whole_number_check {
Some(0) => Some((a, b_plus_c)),
Some(_) => None,
None => None,
}
}).collect::<Vec<(u32; 2)>>();
a_b_plus_c.into_par_iter().filter_map(|a, b_plus_c| {
let b: u32 = (b_plus_c.pow(2) - a.pow(2))/(b_plus_c * 2);
let c: u32 = b_plus_c - b;
match b {
b if b > a => [a, b, c]
_ => None,
}}
).collect::<HashSet<[u32; 3]>>();
}
Or rather, it would be if it worked...
The current issue is in the line:
let a_b_plus_c: Vec<(u32; 2)> = (1_u32..(sum / 3_u32)).into_par_iter()
It says that it expected one of a number of symbols when parsing the type for a_b_plus_c, but found ;. From everything that I've seen (not much), this is the correct way to define a vector of tuples, each of which has two elements of type u32.
As I said, this is a learning exercise for me, so if anybody could help me out, I would be grateful for verbose and detailed answers :)
For what it's worth, as it might help you to comment on my code, this is the maths:
a + b + c = sum
a² + b² = c²
Rearrange for b:
b = ((b + c)² - a²) / (2(b + c))
So, iterate through a to get b+c, since (b+c) = sum - a
Then solve the above equation to get a, b+c, and b
Confirm that a < b
Then solve for c:
c = (b + c) - b
It should then spit them all out into a HashSet of arrays of a,b,c
You should enumerate each tuple's element type in definition. This should work:
let a_b_plus_c: Vec<(u32, u32)> = (1_u32..(sum / 3_u32)).into_par_iter()
I'm wondering about methods of mapping multiple arrays into one list of object.
I mean e.g. I have
val a = arrayOf("A1","A2","A3")
val b = arrayOf("B1","B2","B3")
and
data class SomeClass(val v1:String, val v2:String)
I want to parse it in elegant way to have list like that:
val list = listOf(SomeClass("A1","B1"),SomeClass("A2","B2"),SomeClass("A3","B3"))
I assume they are of the same length. The only way I thought of is:
val list = mutableListOf<SomeClass>()
for (i in a.indices)
array.add(SomeClass(a[i],b[i])
Is there a better, more elegant solution (maybe using Collecions.zip or Array.map)?
Try Array.zip and then map:
val list = a.zip(b)
.map { SomeClass(it.first, it.second) }
or if you like it more:
val list = a.zip(b)
.map { (a, b) -> SomeClass(a, b) }
Note that if both arrays differ in size, the additional values are ignored. Note also that this will create intermediate Pairs (which is the default transformation function of zip). Even though I like the explicit map more, #hotkeys solution regarding the overloaded method is more appropriate (you spare that hidden Pair-transformation):
val list = a.zip(b) { a, b -> SomeClass(a, b) }
And where the overloaded method probably shines, is when using references instead:
a.zip(b, ::SomeClass)
Which will work as long as you have a constructor matching the zipped arguments and doesn't work out of the box for the Pair (yet?).
Improving on #Roland's answer, you can use the zip overload that accepts a two-argument function for mapping the pairs immediately:
val result = a.zip(b) { x, y -> SomeClass(x, y) }
You can write some custom fun like this:
inline fun <T, R, E, V> Iterable<T>.zipThree(other1: Iterable<R>, other2: Iterable<E>, transform: (T, R, E) -> V): List<V> {
val first = iterator()
val second = other1.iterator()
val third = other2.iterator()
val list = ArrayList<V>()
while (first.hasNext() && second.hasNext()) {
list.add(transform(first.next(), second.next(), third.next()))
}
return list
}
And use this transform for getting List
val strings = listOf("1", "2")
val ints = listOf(1, 2)
val boolean = listOf(true, false)
val listYoutObjects = strings.zipThree(ints, boolean) { one, two, three -> YouObject(one, two, three) }
I'd like a function runningSum on an array of numbers a (or any ordered collection of addable things) that returns an array of the same length where each element i is the sum of all elements in A up to an including i.
Examples:
runningSum([1,1,1,1,1,1]) -> [1,2,3,4,5,6]
runningSum([2,2,2,2,2,2]) -> [2,4,6,8,10,12]
runningSum([1,0,1,0,1,0]) -> [1,1,2,2,3,3]
runningSum([0,1,0,1,0,1]) -> [0,1,1,2,2,3]
I can do this with a for loop, or whatever. Is there a more functional option? It's a little like a reduce, except that it builds a result array that has all the intermediate values.
Even more general would be to have a function that takes any sequence and provides a sequence that's the running total of the input sequence.
The general combinator you're looking for is often called scan, and can be defined (like all higher-order functions on lists) in terms of reduce:
extension Array {
func scan<T>(initial: T, _ f: (T, Element) -> T) -> [T] {
return self.reduce([initial], combine: { (listSoFar: [T], next: Element) -> [T] in
// because we seeded it with a non-empty
// list, it's easy to prove inductively
// that this unwrapping can't fail
let lastElement = listSoFar.last!
return listSoFar + [f(lastElement, next)]
})
}
}
(But I would suggest that that's not a very good implementation.)
This is a very useful general function, and it's a shame that it's not included in the standard library.
You can then generate your cumulative sum by specializing the starting value and operation:
let cumSum = els.scan(0, +)
And you can omit the zero-length case rather simply:
let cumSumTail = els.scan(0, +).dropFirst()
Swift 4
The general sequence case
Citing the OP:
Even more general would be to have a function that takes any sequence
and provides a sequence that's the running total of the input
sequence.
Consider some arbitrary sequence (conforming to Sequence), say
var seq = 1... // 1, 2, 3, ... (CountablePartialRangeFrom)
To create another sequence which is the (lazy) running sum over seq, you can make use of the global sequence(state:next:) function:
var runningSumSequence =
sequence(state: (sum: 0, it: seq.makeIterator())) { state -> Int? in
if let val = state.it.next() {
defer { state.sum += val }
return val + state.sum
}
else { return nil }
}
// Consume and print accumulated values less than 100
while let accumulatedSum = runningSumSequence.next(),
accumulatedSum < 100 { print(accumulatedSum) }
// 1 3 6 10 15 21 28 36 45 55 66 78 91
// Consume and print next
print(runningSumSequence.next() ?? -1) // 120
// ...
If we'd like (for the joy of it), we could condense the closure to sequence(state:next:) above somewhat:
var runningSumSequence =
sequence(state: (sum: 0, it: seq.makeIterator())) {
(state: inout (sum: Int, it: AnyIterator<Int>)) -> Int? in
state.it.next().map { (state.sum + $0, state.sum += $0).0 }
}
However, type inference tends to break (still some open bugs, perhaps?) for these single-line returns of sequence(state:next:), forcing us to explicitly specify the type of state, hence the gritty ... in in the closure.
Alternatively: custom sequence accumulator
protocol Accumulatable {
static func +(lhs: Self, rhs: Self) -> Self
}
extension Int : Accumulatable {}
struct AccumulateSequence<T: Sequence>: Sequence, IteratorProtocol
where T.Element: Accumulatable {
var iterator: T.Iterator
var accumulatedValue: T.Element?
init(_ sequence: T) {
self.iterator = sequence.makeIterator()
}
mutating func next() -> T.Element? {
if let val = iterator.next() {
if accumulatedValue == nil {
accumulatedValue = val
}
else { defer { accumulatedValue = accumulatedValue! + val } }
return accumulatedValue
}
return nil
}
}
var accumulator = AccumulateSequence(1...)
// Consume and print accumulated values less than 100
while let accumulatedSum = accumulator.next(),
accumulatedSum < 100 { print(accumulatedSum) }
// 1 3 6 10 15 21 28 36 45 55 66 78 91
The specific array case: using reduce(into:_:)
As of Swift 4, we can use reduce(into:_:) to accumulate the running sum into an array.
let runningSum = arr
.reduce(into: []) { $0.append(($0.last ?? 0) + $1) }
// [2, 4, 6, 8, 10, 12]
By using reduce(into:_:), the [Int] accumulator will not be copied in subsequent reduce iterations; citing the Language reference:
This method is preferred over reduce(_:_:) for efficiency when the
result is a copy-on-write type, for example an Array or a
Dictionary.
See also the implementation of reduce(into:_:), noting that the accumulator is provided as an inout parameter to the supplied closure.
However, each iteration will still result in an append(_:) call on the accumulator array; amortized O(1) averaged over many invocations, but still an arguably unnecessary overhead here as we know the final size of the accumulator.
Because arrays increase their allocated capacity using an exponential
strategy, appending a single element to an array is an O(1) operation
when averaged over many calls to the append(_:) method. When an array
has additional capacity and is not sharing its storage with another
instance, appending an element is O(1). When an array needs to
reallocate storage before appending or its storage is shared with
another copy, appending is O(n), where n is the length of the array.
Thus, knowing the final size of the accumulator, we could explicitly reserve such a capacity for it using reserveCapacity(_:) (as is done e.g. for the native implementation of map(_:))
let runningSum = arr
.reduce(into: [Int]()) { (sums, element) in
if let sum = sums.last {
sums.append(sum + element)
}
else {
sums.reserveCapacity(arr.count)
sums.append(element)
}
} // [2, 4, 6, 8, 10, 12]
For the joy of it, condensed:
let runningSum = arr
.reduce(into: []) {
$0.append(($0.last ?? ($0.reserveCapacity(arr.count), 0).1) + $1)
} // [2, 4, 6, 8, 10, 12]
Swift 3: Using enumerated() for subsequent calls to reduce
Another Swift 3 alternative (with an overhead ...) is using enumerated().map in combination with reduce within each element mapping:
func runningSum(_ arr: [Int]) -> [Int] {
return arr.enumerated().map { arr.prefix($0).reduce($1, +) }
} /* thanks #Hamish for improvement! */
let arr = [2, 2, 2, 2, 2, 2]
print(runningSum(arr)) // [2, 4, 6, 8, 10, 12]
The upside is you wont have to use an array as the collector in a single reduce (instead repeatedly calling reduce).
Just for fun: The running sum as a one-liner:
let arr = [1, 2, 3, 4]
let rs = arr.map({ () -> (Int) -> Int in var s = 0; return { (s += $0, s).1 } }())
print(rs) // [1, 3, 6, 10]
It does the same as the (updated) code in JAL's answer, in particular,
no intermediate arrays are generated.
The sum variable is captured in an immediately-evaluated closure returning the transformation.
If you just want it to work for Int, you can use this:
func runningSum(array: [Int]) -> [Int] {
return array.reduce([], combine: { (sums, element) in
return sums + [element + (sums.last ?? 0)]
})
}
If you want it to be generic over the element type, you have to do a lot of extra work declaring the various number types to conform to a custom protocol that provides a zero element, and (if you want it generic over both floating point and integer types) an addition operation, because Swift doesn't do that already. (A future version of Swift may fix this problem.)
Assuming an array of Ints, sounds like you can use map to manipulate the input:
let arr = [0,1,0,1,0,1]
var sum = 0
let val = arr.map { (sum += $0, sum).1 }
print(val) // "[0, 1, 1, 2, 2, 3]\n"
I'll keep working on a solution that doesn't use an external variable.
I thought I'd be cool to extend Sequence with a generic scan function as is suggested in the great first answer.
Given this extension, you can get the running sum of an array like this: [1,2,3].scan(0, +)
But you can also get other interesting things…
Running product: array.scan(1, *)
Running max: array.scan(Int.min, max)
Running min: array.scan(Int.max, min)
Because the implementation is a function on Sequence and returns a Sequence, you can chain it together with other sequence functions. It is efficient, having linear running time.
Here's the extension…
extension Sequence {
func scan<Result>(_ initialResult: Result, _ nextPartialResult: #escaping (Result, Self.Element) -> Result) -> ScanSequence<Self, Result> {
return ScanSequence(initialResult: initialResult, underlying: self, combine: nextPartialResult)
}
}
struct ScanSequence<Underlying: Sequence, Result>: Sequence {
let initialResult: Result
let underlying: Underlying
let combine: (Result, Underlying.Element) -> Result
typealias Iterator = ScanIterator<Underlying.Iterator, Result>
func makeIterator() -> Iterator {
return ScanIterator(previousResult: initialResult, underlying: underlying.makeIterator(), combine: combine)
}
var underestimatedCount: Int {
return underlying.underestimatedCount
}
}
struct ScanIterator<Underlying: IteratorProtocol, Result>: IteratorProtocol {
var previousResult: Result
var underlying: Underlying
let combine: (Result, Underlying.Element) -> Result
mutating func next() -> Result? {
guard let nextUnderlying = underlying.next() else {
return nil
}
previousResult = combine(previousResult, nextUnderlying)
return previousResult
}
}
One solution using reduce:
func runningSum(array: [Int]) -> [Int] {
return array.reduce([], combine: { (result: [Int], item: Int) -> [Int] in
if result.isEmpty {
return [item] //first item, just take the value
}
// otherwise take the previous value and append the new item
return result + [result.last! + item]
})
}
I'm very late to this party. The other answers have good explanations. But none of them have provided the initial result, in a generic way. This implementation is useful to me.
public extension Sequence {
/// A sequence of the partial results that `reduce` would employ.
func scan<Result>(
_ initialResult: Result,
_ nextPartialResult: #escaping (Result, Element) -> Result
) -> AnySequence<Result> {
var iterator = makeIterator()
return .init(
sequence(first: initialResult) { partialResult in
iterator.next().map {
nextPartialResult(partialResult, $0)
}
}
)
}
}
extension Sequence where Element: AdditiveArithmetic & ExpressibleByIntegerLiteral {
var runningSum: AnySequence<Element> { scan(0, +).dropFirst() }
}