Program crashes when integer is not 0, 1 or 2 - ios

I am a complete beginner to all things programming, so please forgive any blatant mistakes that you may find. I am writing a program that takes an input number and tells the user whether or not the number is a prime.
#IBOutlet weak var textfield: UITextField!
#IBOutlet weak var label: UILabel!
#IBAction func button(sender: AnyObject) {
if (textfield.text.isEmpty) {
label.text = "Please enter a number!"
} else if (textfield.text == "0" || textfield.text == "1"){
label.text = "The answer is undefined."
textfield.text = nil
} else {
var input = textfield.text.toInt()
if (input! % 2 == 0 && input! != 2) {
label.text = "The number is not prime, one of its divisors is 2."
textfield.text = nil
} else if (input! == 2){
label.text = "The number is indeed prime!"
textfield.text = nil
} else {
var factor = 3
while (factor <= input) {
if (input! % factor == 0 && input! != factor) {
label.text = "The number is not prime, one of its divisors is \(factor)"
} else if (input! % factor != 0){
factor += 2
} else if (input! % factor == 0 && input! == factor) {
label.text = "The number is indeed prime!"
textfield.text = nil
}
}
}
}
}
My code looks like this, but crashes if the input is anything that is not 0, 1, or 2. I know there are better ways to write this program but I just want to figure out what exactly is wrong with this one. Thanks in advance!

In the cases where you are updating the label.text you are not incrementing the factor which leads to an infinite loop. Add a break to those cases to break out of the while loop and all will be well:
while (factor <= input!) {
if (input! % factor == 0 && input! != factor) {
label.text = "The number is not prime, one of its divisors is \(factor)"
break
} else if (input! % factor != 0){
factor += 2
} else if (input! % factor == 0 && input! == factor) {
label.text = "The number is indeed prime!"
textfield.text = nil
break
}
}

It not your swift. It is your programming logic. Your are not crashing, you are hanging in an infinite
loop.
put a println in you while loop.
while (factor <= input) {
println ("Factor (input! % factor)")
Factor 0
Factor 0
Factor 0
Factor 0
You never end up incrementing factor. In my test case I enter 3 as the input.
David

I think the input return a optional variable.
var input = textfield.text.toInt()
//and you force unwrap the input on the this statement:
if (input! % 2 == 0 && input! != 2) {}
I think it crashed on here if the text is not number.
Take a look of the extension of String
extension String {
/// If the string represents an integer that fits into an Int, returns
/// the corresponding integer. This accepts strings that match the regular
/// expression "[-+]?[0-9]+" only.
func toInt() -> Int?
}

Related

Tracking specific data in string using Swift

I'm still new to coding in general, and I'm running into an issue with a Swift exercise.
How do I track the number of times 1 (var numberOfSteps) and the number of times 2 (var numberOfHeartBeats) appears in the string "12221231221"?
I'm given the following hint but not sure how the for-in loop applies:
let activityData = "12221231221"
var numberOfSteps = 0
var numberOfHeartBeats = 0
for character in activityData{
print(character)
}
In the loop, you can check what the current characters is, then increment the variable accordingly:
let activityData = "12221231221"
var numberOfSteps = 0
var numberOfHeartBeats = 0
for character in activityData{
if character == "1" {
numberOfSteps += 1
} else if character == "2" {
numberOfHeartBeats += 1
}
}
Here is another more functional way of doing this:
let dict = Dictionary(grouping: activityData, by: { $0 }).mapValues { $0.count }
dict["1"] is numberOfSteps and dict["2"] is numberOfHeartBeats.
If you want to use a for loop you can do
let activityData = "12221231221"
var numberOfSteps = 0
var numberOfHeartBeats = 0
for character in activityData{
switch character {
case "1":
numberOfSteps += 1
case "2":
numberOfHeartBeats += 1
default:
break
}
}
print("Number of steps: \(numberOfSteps), number of hartbeats: \(numberOfHeartBeats)")
A more condensed solution without a loop
print("Number of steps: \( activityData.filter( {$0 == "1"} ).count), number of hartbeats: \( activityData.filter( {$0 == "2"} ).count)")
You can do this in simple way like this:-
let occurrencesOne = text.characters.filter { $0 == "1" }.count
let occurrencesTwo = text.characters.filter { $0 == "2" }.count
Another solution is to use reduce, this is kinda equivalent to manually looping over the string, but will less and mode concise code, and without the disadvantage of creating intermediary structures (like arrays of dictionaries), which for large inputs can be problematic memory-wise:
let activityData = "12221231221"
let numberOfSteps = activityData.reduce(0) { $1 == "1" ? $0 + 1 : $0 }
let numberOfHeartBeats = activityData.reduce(0) { $1 == "2" ? $0 + 1 : $0 }
print(numberOfSteps, numberOfHeartBeats) // 4, 6
One step further to reduce the duplication would be to add a function to compute the reduce second parameter:
func countIf<T: Equatable>(_ search: T) -> (Int, T) -> Int {
return { $1 == search ? $0 + 1 : $0 }
}
let activityData = "12221231221"
let numberOfSteps: Int = activityData.reduce(0, countIf("1"))
let numberOfHeartBeats = activityData.reduce(0, countIf("2"))
print(numberOfSteps, numberOfHeartBeats)

Error when add negative numbers in calculator

I have this code:
class MainViewController: UIViewController {
#IBOutlet weak var summaryLbl: UILabel!
var actualNumber: Double = 0
var previousNumber: Double = 0
var operationMath: Bool = false
var operation = 0
#IBAction func numberPressed(_ sender: UIButton) {
if operationMath == true {
summaryLbl.text = String(sender.tag)
actualNumber = Double(summaryLbl.text!)!
operationMath = false
} else {
if summaryLbl.text == "0" {
summaryLbl.text = ""
}
summaryLbl.text = summaryLbl.text! + String(sender.tag)
actualNumber = Double(summaryLbl.text!)!
}
}
#IBAction func buttons(_ sender: UIButton) {
if summaryLbl.text != "" && sender.tag != 10 && sender.tag != 17 {
previousNumber = Double(summaryLbl.text!)!
if sender.tag == 13 {
summaryLbl.text = "/"
} else if sender.tag == 14 {
summaryLbl.text = "x"
} else if sender.tag == 15 {
summaryLbl.text = "-"
} else if sender.tag == 16 {
summaryLbl.text = "+"
} else if sender.tag == 11 {
var number: Double = Double(summaryLbl.text!)!
number.negate()
let rounded = number.rounded()
summaryLbl.text = String(rounded).replacingOccurrences(of: ".0", with: "", options: .literal, range: nil)
}
operation = sender.tag
operationMath = true
} else if sender.tag == 17 {
var result: Double = 0
var rounded: Double = 0
if operation == 13 {
result = previousNumber / actualNumber
} else if operation == 14 {
result = previousNumber * actualNumber
} else if operation == 15 {
result = previousNumber - actualNumber
} else if operation == 16 {
result = previousNumber + actualNumber
} else if operation == 12 {
result = previousNumber.truncatingRemainder(dividingBy: actualNumber)
}
rounded = result.rounded()
if (result == rounded) {
summaryLbl.text = String(result).replacingOccurrences(of: ".0", with: "", options: .literal, range: nil)
} else {
summaryLbl.text = String(result)
}
} else if sender.tag == 10 {
summaryLbl.text = "0"
previousNumber = 0
actualNumber = 0
operation = 0
}
}
override func viewDidLoad() {
super.viewDidLoad()
summaryLbl.text = "0"
previousNumber = 0
actualNumber = 0
operation = 0
}
}
This is simple calculator.
I have a problem with calculations.
When I click the buttons, for example: 2 + 5 * -
then the application turns off with an error. When I enter such a key combination: 2 + 5 =
This calculation will be done correctly.
 
How do I add commas to numbers?
Does anyone know how to fix the above problems?
A calculator is a Finite State Machine. It can be very complex but in its simplest form it resembles this:
So if we keep things simple and take the above machine as our target, after 2 + 5, our machine expects equals(=) to calculate the result or if an operator is added (like * in our case) it will expect a digit next. giving an operator (minus in our case) will result in an error.
The complexity is limited only by your imagination. You can add support for decimal point numbers, brackets, powers etc. The more sugar you want to add the more complex the FSM will become.
I suggest starting with the simplest one. Maintain your states, the transitions allowed next and error handling in case of wrong transition.
Check this repo on github for Finite State Machine in swift: https://github.com/vishalvshekkar/SwiftFSM
And the corresponding article:
https://blog.vishalvshekkar.com/finite-state-machine-in-swift-ba0958bca34f

Number formatter not allowing decimals to show

Here is the obligatory "I'm new to programming" but, I've searched all available answers and have concluded that my issue may be more logic related than code, but I could be wrong about that too. I'm building a calculator app and everything is working except the numberFormatter (to show comma separators) in the display. Whenever I try to format the number in the display, I can't get the display to show the decimal and the commas.
If I start with a decimal .1234 , I get 0.1234 and if I type 12345 I get 12,345 but if i type 12345.678, I get 12,345. I'm losing the decimals. I've tested it and my function to remove extraneous "." doesn't seem to be the issue. And If I run the string extension numberFormatter outside of the label formatting controls it seems to work, but I need to guard against multiple decimals and extraneous "0"s.
I'm showing the code to the IBAction covering the buttons showing up on the display label, display.text which is the issue. All calculations after this are working fine, with the replacingOccurrences(of: ",", with: "") to create a clean string to convert to Double and calculate.
I'm using a sting extension to do the formatting. I've been working on and off on this for weeks. Any ideas? Do I have to refactor how I enter text into the label.text?
here is the code to add text to the UILabel display.
#IBAction func btnTouchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
if isUserTyping {
var formattedNumber = ""
print( "is user typting + String\(isUserTyping)")
// make sure we aren't adding a second period
var textCurrentlyInDisplay = display.text
textCurrentlyInDisplay = textCurrentlyInDisplay?.replacingOccurrences(of: ",", with: "")
if digit == "." && ((textCurrentlyInDisplay?.range(of: ".")) != nil) {
return
}
else {
formattedNumber = (textCurrentlyInDisplay! + digit)
print("formattedNumber = \(formattedNumber.twoFractionDigits)")
display.text = formattedNumber.twoFractionDigits
// put code here to format label.text to show thousand seperators
print("textCurrentlyInDisplay end = \(textCurrentlyInDisplay!)")
}
}
// make sure we aren't entering a bunch of zero's
else { print("else + \(isUserTyping)")
display.text = digit
if digit == "0" {return}
else if digit == "." {display.text = "0."}
// display.text = (digit == "." ? "0" : "") + digit
isUserTyping = true
}
}
Here is my extension to handle the string conversion for the numberFormatter.
extension String {
var twoFractionDigits: String {
let styler = NumberFormatter()
styler.minimumFractionDigits = 0
styler.maximumFractionDigits = 16
styler.numberStyle = .decimal
let converter = NumberFormatter()
converter.decimalSeparator = "."
if let result = converter.number(from: self) {
return styler.string(from: result)!
}
return ""
}
I found a hack to work around my problem. It's not pretty, but it works. I was able to get the numberFormatter to update and show the digits after the decimal but that led to a new issue. If you typed 12345.00 you would get 12,344 and not see the trailing 0's until you pressed another number. ex 12345.1 -> 12,345.1, 12345.001 -> 12,345.001, but 12345.00 -> 12,345.
I needed it to work dynamically so the user knows how many zeros are being entered. My hack was to split the final amount into an two arrays. One pre-formatted and one post-formatted. Then join the two together for the final display number.
I'd still love to find a more efficient solution, if anyone has any ideas.
Here is the updated code.
#IBAction func btnTouchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
if isUserTyping {
preFormattedNumber = (display.text?.replacingOccurrences(of: ",", with: ""))!
// set the limit for number of digits user can enter at once
if display.text!.count >= 16 {
return
}
else {
// make sure we aren't adding a second period
if digit == "." && ((preFormattedNumber.range(of: ".")) != nil) {
print("extra decimal pressed")
return
}
else {
preFormattedNumber = (preFormattedNumber + digit)
print("preFormattedNumber before Formatting = \(preFormattedNumber)")
}
// put code here to format label.text to show thousand seperators
if ((preFormattedNumber.range(of: ".")) != nil){
print("just checked for .")
let numPreFormat = preFormattedNumber
let numAfterFormat = preFormattedNumber.twoFractionDigits
let numArray = numPreFormat.components(separatedBy: ".")
let numArrayFormatted = numAfterFormat.components(separatedBy: ".")
let afterDecimal = numArray.last
let beforeDecimal = numArrayFormatted.first
let finalNumberToDisplay = beforeDecimal! + "." + afterDecimal!
print("numArray = \(numArray)")
print("final number to display = \(finalNumberToDisplay)")
print("numArray = \(numArray)")
display.text = finalNumberToDisplay
runningNumber = display.text!
}
else {
display.text = preFormattedNumber.twoFractionDigits
runningNumber = display.text!
}
}
}
// make sure we aren't entering a bunch of zero's
else { print("else + \(isUserTyping)")
preFormattedNumber = digit
display.text = preFormattedNumber
runningNumber = display.text!
if digit == "0" {return}
else if digit == "." { preFormattedNumber = "0.";
}
display.text = preFormattedNumber
runningNumber = display.text!
isUserTyping = true
}
}

UISegmentedControl and .selectedSegmentIndex wrong values?

I have a simple Segment in my code with 3 elements. For testing purposes I also do have a variable that increments based on which of the segments I press (3). The value of that variable is printed in a UITextView. This is the code:
import UIKit
class ViewController: UIViewController
{
#IBOutlet weak var segment: UISegmentedControl!
#IBOutlet weak var prwtoView: UIView!
#IBOutlet weak var prwtoText: UITextField!
var i : Int = 0
override func viewWillAppear(animated: Bool)
{
prwtoText.backgroundColor = UIColor.purpleColor()
prwtoText.textColor = UIColor.whiteColor()
segment.setTitle("Zero", forSegmentAtIndex: 0)
segment.addTarget(self, action: "action", forControlEvents: .ValueChanged)
segment.insertSegmentWithTitle("random", atIndex: 2, animated: false)
}
func action()
{
let argumentForSegment = segment.selectedSegmentIndex
if argumentForSegment == 0
{
i = 0
}
if argumentForSegment == 1
{
i += 2
}
else
{
i += Int(arc4random_uniform(100))
}
print(argumentForSegment)
prwtoText.text = "\(i)"
}
}
While I know that it starts with value -1 i don't want my app to do anything if not pressed. The thing is that even when I press the first segment (0) and it is supposed to make i = 0 it doesn't do that, although if I print argumentForSegment in my terminal it does show the 0 as value. Concluding, every time I press the zero segment (0), my i value won't become 0. Perhaps I am using the wrong method from UISegmentedControl?
edit: Got it fixed by changing the following code:
func action()
{
let argumentForSegment = segment.selectedSegmentIndex
if argumentForSegment == 0
{
i = 0
}
if argumentForSegment == 1
{
i += 2
}
else
{
i += Int(arc4random_uniform(100))
}
print(argumentForSegment)
prwtoText.text = "\(i)"
}
to:
func action()
{
let argumentForSegment = segment.selectedSegmentIndex
if argumentForSegment == 0
{
i = 0
}
if argumentForSegment == 1
{
i += 2
}
else if argumentForSegment == 2 // <==== here
{
i += Int(arc4random_uniform(100))
}
print(argumentForSegment)
prwtoText.text = "\(i)"
}
Could someone explain why it used the priority of else although the value was zero when printing argumentForSegment? In other words why when I had an else alone for the value of argumentForSegment == 0 it chose the else instead of the first statement?
Could someone explain why it used the priority of else although the
value was zero when printing argumentForSegment? In other words why
when I had an else alone for the value of argumentForSegment == 0 it
chose the else instead of the first statement?
When you have a situation where the code is not behaving as you expect, it is helpful to step through it in the debugger, or add some diagnostic print statements.
For example:
func action()
{
let argumentForSegment = segment.selectedSegmentIndex
if argumentForSegment == 0
{
print("In first block")
i = 0
}
if argumentForSegment == 1
{
print("In second block")
i += 2
}
else
{
print("In third block")
i += Int(arc4random_uniform(100))
}
print(argumentForSegment)
prwtoText.text = "\(i)"
}
If you do this, you will notice that when argumentForSegment is 0, the output will be:
In first block
In third block
So, the problem is not that it is choosing the third block over the first. The problem is that it is doing both. You want it to stop after it has detected that argumentForSegment is 0, so add an else to the second conditional statement so that it only does that when the first conditional statement failed:
func action()
{
let argumentForSegment = segment.selectedSegmentIndex
if argumentForSegment == 0
{
i = 0
}
else if argumentForSegment == 1 // added "else" here
{
i += 2
}
else
{
i += Int(arc4random_uniform(100))
}
print(argumentForSegment)
prwtoText.text = "\(i)"
}
To improve on Vacawama's answer, you can format this much easier by using a switch statement:
func action() {
let argumentForSegment = segment.selectedSegmentIndex
switch argumentForSegment {
case 0:
i = 0
case 1:
i += 1
case 2:
i += Int(arc4random_uniform(100))
default:
break
}
print(argumentForSegment)
prwtoText.text = "\(i)"
}
it's much more clean for this type of thing.
(thanks, vacawama)

Prime number checker returns the same result each time

I'm a beginner programmer learning Swift and made a basic prime number checker. No matter what it will only give one result, instead of changing based on wether or not the number is prime. Any help would be appreciated.
#IBAction func primeCheck(sender: AnyObject) {
var numberInt = number.text.toInt()
var isPrime = true
if number != nil {
if numberInt == 1 {
isPrime = false
}
if numberInt != 1 {
for var i = 2; i < numberInt; i++ {
if numberInt! % i == 0 {
isPrime = false
} else {
isPrime = true
}
}
}
}
if isPrime == true {
result.text = "\(numberInt!) is a prime number!"
} else {
result.text = "\(numberInt!) is not a prime number!"
}
}
I have another possible solution. At first I divide by two because it cannot be a prime number. Then you loop until the number is prime or the number divided by two is less than the divider.
#IBAction func primeCheck(sender: AnyObject) {
var numberInt = number.text.toInt()
var isPrime = true
var divider = 3
if number < 2 || (number != 2 && number % 2 == 0) {
isPrime = false
}
// you only have to check to half of the number
while(isPrime == true && divider < number / 2){
isPrime = number % divider != 0
divider += 2
}
if isPrime == true {
result.text = "\(numberInt!) is a prime number!"
} else {
result.text = "\(numberInt!) is not a prime number!"
}
}
The error in your logic comes in this section:
if numberInt! % i == 0 {
isPrime = false
} else {
isPrime = true
}
At the top of your function, you initialize isPrime to be true, so in your loop you only need to look for cases that prove the number is not prime. You don't ever need to set isPrime = true again, so just drop the else condition:
if numberInt! % i == 0 {
isPrime = false
}
You actually have two functions here. One to check if a number is prime and the other to display the result. Separating these makes everything much easier to manage.
// function to check primality and return a bool
// note that this can only accept a non optional Int so there is
// no need to check whether it is valid etc...
func checkNumberIsPrime(number: Int) -> Bool {
// get rid of trivial examples to improve the speed later
if number == 2 || number == 3 {
return true
}
if number <= 1 || number%2 == 0 {
return false
}
// square root and round up to the nearest int
let squareRoot: Int = Int(ceil(sqrtf(Float(number))))
// no need to check anything above sqrt of number
// any factor above the square root will have a cofactor
// below the square root.
// don't need to check even numbers because we already checked for 2
// half the numbers checked = twice as fast :-D
for i in stride(from: 3, to: squareRoot, by: 2) {
if number % i == 0 {
return false
}
}
return true
}
// function on the button. Run the check and display results.
#IBAction func primeCheck(sender: AnyObject) {
let numberInt? = numberTextField.text.toInt() // don't call a text field "number", it's just confusing.
if let actualNumber = numberInt {
if checkNumberIsPrime(actualNumber) {
resultLabel.text = "\(actualNumber) is a prime number!" // don't call a label "result" call it "resultLabel". Don't confuse things.
} else {
resultLabel.text = "\(actualNumber) is not a prime number!"
}
} else {
resultLabel.text = "'\(numberTextField.text)' is not a number!"
}
}
It makes it all much easy to read and maintain.
You have to break out of the loop after you find that the number is divisble by another number. Also for prime check you only have to check the divisibility till the square root of the number.
You can also use optional binding to extract numberInt and check for nil. That's the swift way.
#IBAction func primeCheck(sender: AnyObject) {
var isPrime = true
if let numberInt = number.text.toInt() {
if numberInt == 1 {
isPrime = false /
}
else // Add else because you dont have to execute code below if number is 1
{
if numberInt != 1 {
for var i = 2; i * i <= numberInt; i++ { // Only check till squareroot
if numberInt % i == 0 {
isPrime = false
break // Break out of loop if number is divisible.
} // Don't need else condition because isPrime is initialised as true.
}
}
}
if isPrime {
result.text = "\(numberInt) is a prime number!"
} else {
result.text = "\(numberInt) is not a prime number!"
}
}
}
Reason for square root check : Why do we check up to the square root of a prime number to determine if it is prime?
You can refine the code further by refactoring the prime check into a separate function.
func isPrime(number:Int) -> Bool
{
if number == 1 {
return false
}
else
{
if number != 1 {
for var i = 2; i * i <= numberInt; i++ {
if numberInt % i == 0 {
return false
}
}
}
}
return true
}
#IBAction func primeCheck(sender: AnyObject) {
if let numberInt = number.text.toInt() {
if isPrime(numberInt) {
result.text = "\(numberInt) is a prime number!"
} else {
result.text = "\(numberInt) is not a prime number!"
}
}
}
Well i don't know about swift, but maybe this is wrecking your code:
if numberInt! <<
To do a faster algorithm you could just search for divisors from 2 to sqrt(numberInt). (Theorem)

Resources