0

I have a file with fractions. I have to write fractions from it into a structure. Then I need to create a dynamic data structure in order to add those fractions that have a common denominator, but I should not use dynamic arrays. Can you please help me with the fraction addition part? (the link contains a photo of the file with fractions and the result I want to get, but for now I can only display fractions on the screen, but not add them up) enter image description here

#include <iostream>
#include <stdio.h>
#include <Windows.h>
#include <string.h>

using namespace std;

struct fraction {
    int numerator, denominator;
};

struct Node {
    fraction fr;
    Node* next;
};

Node* newNode(fraction fr) {
    Node* p;
    p = new Node;
    p->fr.numerator = fr.numerator;
    p->fr.denominator = fr.denominator;
    p->next = NULL;
    return p;
}

void addNode(Node* curr, Node** head, Node** tail) {
    if (*head == NULL) {
        *head = *tail = curr;
        //        *tail = curr;
    }
    else {
        (*tail)->next = curr;
        *tail = curr;
    }
}

void outList(Node* head) {
    Node* curr;
    curr = head;
    while (curr != NULL) {
        cout << curr->fr.numerator << "/" << curr->fr.denominator << endl;;
        curr = curr->next;
    }
}

int main() {
    fraction fr;
    int n;
    Node* curr = NULL;
    Node* head = NULL;
    Node* tail = NULL;
    FILE* f;
    fopen_s(&f, "testfile.txt", "r");

    if (f) {
        while (!feof(f)) {
            fscanf_s(f, "%d", &n);
            for (int i = 0; i < n; i++) {
                fscanf_s(f, "%d", &fr.numerator);
                fscanf_s(f, "%d", &fr.denominator);
                curr = newNode(fr);
                addNode(curr, &head, &tail);
            }
        }

        fclose(f);
        outList(head);
    }
}
Chris
  • 26,361
  • 5
  • 21
  • 42
Welisack
  • 15
  • 4
  • Remove the `tail` everywhere, you don't need it. `newNode` should be the `Node`s constructor. And `addNode` should compare the denominator and only add a new node when the denominator doesn't already exist. If you are smart insert the nodes sorted by denominator, will double the speed on average. And looks better in the output. Extra bonus gumi points if you normalize the list, e.g. 3/6 should become 1/2 and 5/4 should become 1/1 + 1/4. – Goswin von Brederlow May 27 '22 at 19:39
  • 1
    Unrelated: [Why is “while ( !feof (file) )” always wrong?](https://stackoverflow.com/questions/5431941) – user4581301 May 27 '22 at 19:41
  • When copying objects, you don't need to copy each member. Try this: `p->fr = fr;`. – Thomas Matthews May 27 '22 at 19:43
  • Since you tagged as C++, you should overload `operator>>` and `operator<<` in your `fraction` and `Node` classes. This would allow you to do something like ``std::cout << fr << std::endl;`. – Thomas Matthews May 27 '22 at 19:46
  • I recommend updating your `fraction` class with methods to compare the denominators and to add two fractions. – Thomas Matthews May 27 '22 at 19:49
  • ok, thanks for the tips on how to improve the code, I'll try it, but I would be grateful if they helped me with the addition of fractions. – Welisack May 27 '22 at 19:53
  • @ThomasMatthews I'm new to this, can you tell me more details, please? – Welisack May 27 '22 at 19:56
  • Also, don't cross the streams. Use all pure C I/O (`#include `) or use all pure C++ I/O (`#include ` and `#include `). Crossing the streams could be bad, like really bad. – Thomas Matthews May 27 '22 at 21:40

2 Answers2

0

I recommend working on the foundation first, the Fraction class. If you need to write code to access the members, maybe you are violating the encapsulation rule or the tight coupling rule.

class Fraction
{
    int   m_numerator;
    int   m_denominator;
    public:  
    Fraction();  // Default constructor, creates 0/1 fraction.
    Fraction(int numerator, int denominator);
    Fraction(const Fraction& f);  // Copy constructor
    bool  Equal_Denominators(const Fraction& f);
    friend std::ostream& operator<<(std::ostream& out, const Fraction& f);
};

Fraction::Fraction()
: m_numerator(0), m_denominator(1)
{ ; }

Fraction::Fraction(int numerator, int m_denominator)
: m_numerator(numerator), m_denominator(denominator)
{
    if (m_denominator == 0)
    {
       // Throw an exception
    }
}

Fraction::Fraction(const Fraction& f)
: m_numerator(f.m_numerator), m_denominator(f.m_denominator)
{ ; }

bool  Fraction::Equal_Denominators(const Fraction& f)
{
    return m_denomenator == f.m_denomenator;
}

std::ostream& operator<<(std::ostream& out, const Fraction& f)
{
    out << "(" << f.m_numerator << "/" << "f.m_denominator" << ")";
    return out;
}

The above code snippet gives methods for compare two fractions for equal denominators, constructing fractions and printing a fraction.

When you are traversing your list you could do something like this:

if (p->fr.Equal_Denominators(fr))
{
    // Add the fractions
}

You could also add some debugging by doing this:

while (p != nullptr)
{
    std::cout << p->fr << "\n";
}

Recommended tasks for O.P.:

  1. Overload operator>> for reading fractions from std::cin or a file.
  2. Overload operator+= for adding and assigning fractions.
  3. Overload operator=.
Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • To be honest, I started learning c++ a couple of months ago and in general I've been programming recently and I don't really understand you. I don't think I have a problem with member access – Welisack May 27 '22 at 20:05
  • but, probably, the fact that my code works correctly does not mean that everything is correct... I can't add fractions with my code? (sorry for my English) – Welisack May 27 '22 at 20:08
  • If you are learning C++ by using C language features like `FILE *` and `fscanf`, I highly recommend a different book or instructor. – Thomas Matthews May 27 '22 at 20:14
  • In order to add fractions, you'll need to test if the fractions have *Equal Denominators*, or if they can be converted to equal denominators. For example, you can add 1/2 and `1/4` together, though they have different denominators. – Thomas Matthews May 27 '22 at 20:16
  • Thanks! Can you please tell me why you need this "Fraction::"? – Welisack May 27 '22 at 20:18
  • sorry to confuse you, in fact I only need to add fractions with the same denominator, I should not add 1/2 and 1/4 bringing these fractions to the same denominator, although I can do it. In fact, all this is just a small task for a beginner. To be honest, I was hoping for something more understandable and similar not my code, but I'll try to parse your code. – Welisack May 27 '22 at 20:22
  • in fact, I'm not trying to write in pure c++, I forgot to specify c in the tags – Welisack May 27 '22 at 20:24
  • I didn't understand the "while" part at all (But anyway, thanks a lot for the help, although your code is above my level of understanding and knowledge of c++ – Welisack May 27 '22 at 20:34
  • Either learn pure C or learn pure C++. Beginners and most programmers should not mix the two languages. Mixing the languages makes the program more complex and complex programs have more injected defects to find. – Thomas Matthews May 27 '22 at 21:33
  • The `Fraction::` is using the *scope resolution operator ::*, and helps the linker find methods that are implemented outside the scope of the class. I prefer placing the method definition in a source file and the class declaration in a header file. If I had the definitions in the header file and make a change to a method, then all files that include the header will be rebuilt. If I make a change to the source file, then only the source file needs to be rebuilt. – Thomas Matthews May 27 '22 at 21:37
0

If you build it up from the bottom and use constructors and operators it can turn into this:

#include <string>
#include <iostream>
#include <cassert>

struct Fraction {
    // construct a Fraction from whole number or n, d
    constexpr Fraction(int n, int d=1) noexcept : numerator(n), denominator(d) { }

    // add other Fraction with matching denominator
    constexpr Fraction operator +=(const Fraction &other) noexcept {
        assert(denominator == other.denominator);
        numerator += other.numerator;
        return *this;
    }

    // denominator may not change but numerator does
    int numerator;
    const int denominator;
};

// print Fraction nicely
std::ostream & operator <<(std::ostream &out, const Fraction &fr) {
    out << "(" << fr.numerator << " / " << fr.denominator << ")";
    return out;
}

struct List {
    struct Node {
        // create Node containing fraction and attach to parent
        constexpr Node(Node **parent, const Fraction &fr_) noexcept : next{*parent}, fr(fr_) {
            *parent = this;
        }

        // printing node just prints fraction
        std::ostream & print(std::ostream &out) const {
            return out << fr;
        }

        // Nodes are always constructed as tail so initialize to nullptr
        Node *next{nullptr};
        Fraction fr;
    };

    // construct empty List
    List() { }

    // no copying List
    List(const List &) = delete;

    // no copy asignment
    List & operator =(const List &) = delete;

    // move construct List by taking over Nodes from other
    List(List && other) : head(other.head) {
        other.head = nullptr;
    }

    // move assign List by swapping Nodes with other
    List & operator =(List &&other) {
        std::swap(head, other.head);
        return *this;
    }

    // deconstruct List by deleting all Nodes
    ~List() noexcept {
        while(head) {
            Node *t = head;
            head = head->next;
            delete t;
        }
    }

    // print List as sum of fractions
    std::ostream & print(std::ostream &out) const {
        for (Node *node = head; node; node = node->next) {
            node->print(out);
            if (node->next) out << " + ";
        }
        return out;
    }

    // add Fraction to list: Either adds to existing Fraction or
    // adds new Node. Fractions are sorted by denominator.
    List & operator +=(const Fraction &fr) {
        Node **tail;
        for (tail = &head; *tail; tail = &((*tail)->next)) {
            int d = (*tail)->fr.denominator;
            if (d > fr.denominator) break; // denominator too large, insert Fraction before
            if (d == fr.denominator) { // denominator matched, += Fraction
                (*tail)->fr += fr;
                return *this;
            }
        }
        // no matching denominator, add new node
        new Node(tail, fr);
        return *this;
    }

    // add Fraction to List and move result to return value 
    List && operator +(const Fraction &other) && {
        (*this) += other;
        return std::move(*this);
    }

    // initialize as empty list
    Node *head{nullptr};
};

// print list
std::ostream & operator <<(std::ostream &out, const List &list) {
    return list.print(out);
}

// sum of 2 fraction gives a List    
List operator +(const Fraction &lhs, const Fraction &rhs) {
    return List{} + lhs + rhs;
}

// tired of typing so much, shorten name
using F = Fraction;

int main() {
    // You can just print sums of fractions like this
    // std::cout << F{1, 5} + F{4, 7} + F{3, 5} + F{1, 2} + F{2, 2} + F{2, 5} << std::endl;

    // or read from stdin
    List list;
    int count;
    std::cin >> count;
    while(count-- > 0) {
        int n, d;
        std::cin >> n >> d;
        list += F{n, d};
    }
    std::cout << list << std::endl;
}
Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42