aboutsummaryrefslogtreecommitdiff
path: root/challenge-197/james-smith/README.md
blob: f6212bf7cdd9803a72235b892d0a9f0a4c03a41f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
[< Previous 196](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-196/james-smith) |
[Next 198 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-198/james-smith)

# The Weekly Challenge 197

You can find more information about this weeks, and previous weeks challenges at:

  https://theweeklychallenge.org/

If you are not already doing the challenge - it is a good place to practise your
**perl** or **raku**. If it is not **perl** or **raku** you develop in - you can
submit solutions in whichever language you feel comfortable with.

You can find the solutions here on github at:

https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-196/james-smith

# Task 1 - Move Zero

***You are given a list of integers, `@list`. Write a script to move all zero, if exists, to the end while maintaining the relative order of non-zero elements.***

## Solution
I looked at a number of solutions for this - but it turns out that perl grep seems to be the best...

```perl
sub move_zero{
  grep({$_}@_),grep{!$_}@_
}
```
Using `$_` and `!$_` to pull the lists apart. Anything more complex in the grep slows it down more than you lose by doing the second `grep`.

# Task 2 - Wiggle sort

***You are given a list of integers, `@list`. Write a script to perform Wiggle Sort on the given list. Wiggle sort would be such as:***

```
list[0] < list[1] > list[2] < list[3]…
```

## Solution

OK if we relax the condition with `<=`/`=>` rather than `<`/`>` we can always come up with a solution. The simplest way to do this is to split the list in two (using `splice` and then stitching them back together. (If it has an odd length we keep the first list as the longest!)

```perl
sub ws_lax {
  return @_ if @_<2; ## Always works if 0/1 element.
  my@q=splice @_,0,$#_/2+1;
  map{$_,@_?shift:()}@q
}
```

If we wish to perform the strict version we have to test conditions for which there are no solution...

 * If we have more than half (if even length) or *half + 1* (if odd) of the lowest digit we have no solution.
 * o/w if we have exactly half (if even length) or *half + 1* (if odd) of the lowest digit we have a solution.
 * o/w if we have more than half (if even length) or *half - 1* (if odd) of the highest digit we have no solution
 * o/w if we have exactly than half (if even length) or *half - 1* (if odd) of the highest digit we have no solution
 * o/w if we have half or more (if even length) or *half - 1* or more (if odd) of any other digit we have no solution
 * o/w we have a solution

This leads us to:

```perl
sub _ws {
## Does wiggle sort by splicing and interleaving sorted list...
  my@q=splice @_,0,$#_/2+1;map{$_,@_?shift:()}@q
}

sub ws_strict {
## Return "wiggle sorted list" or error message indicating if
## the problem number is the first number, last number or one
## of the other numbers...

  return @_ if @_<2;
  @_=sort{$a<=>$b} @_;
  return $_[0]  == $_[$#_/2+1] ?                 'Bottom' # We can't have more than ceil(n/2) of the first number
       : $_[0]  == $_[$#_/2  ] ?                 _ws(@_)  # But we can have ceil(n/2) of the first number
       : $_[-1] == $_[$#_/2  ] ?                 'Top'    # We can't have more than floor(n/2) of the last number
       : $_[-1] == $_[$#_/2+1] ?                 _ws(@_)  # But we can have floor(n/2) of it
       : (grep{$_[$_]==$_[$_+$#_/2]}0..@_/2-1) ? 'Middle' # We can't have equal or more than floor(n/2) of any other number
       :                                         _ws(@_)
       ;
}

sub ws_lax {
## Return "wiggle sorted list" - note we are using the lax <= => check here
  @_<2?@_:_ws(sort{$a<=>$b}@_)
}
```